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

systemd / systemd / 21192089104

20 Jan 2026 11:35PM UTC coverage: 72.524% (-0.3%) from 72.818%
21192089104

push

github

yuwata
mkdir: reset mtime *after* fchown()

Follow-up for 34c3d5747

Also, drop pointless shortcut.

1 of 2 new or added lines in 1 file covered. (50.0%)

2960 existing lines in 48 files now uncovered.

309808 of 427181 relevant lines covered (72.52%)

1236537.64 hits per line

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

79.46
/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 "dissect-image.h"
22
#include "dynamic-user.h"
23
#include "env-file.h"
24
#include "env-util.h"
25
#include "escape.h"
26
#include "execute.h"
27
#include "execute-serialize.h"
28
#include "fd-util.h"
29
#include "fdset.h"
30
#include "fileio.h"
31
#include "format-util.h"
32
#include "fs-util.h"
33
#include "glob-util.h"
34
#include "hexdecoct.h"
35
#include "image-policy.h"
36
#include "io-util.h"
37
#include "ioprio-util.h"
38
#include "log.h"
39
#include "manager.h"
40
#include "mkdir.h"
41
#include "namespace-util.h"
42
#include "namespace.h"
43
#include "nsflags.h"
44
#include "open-file.h"
45
#include "ordered-set.h"
46
#include "osc-context.h"
47
#include "parse-util.h"
48
#include "path-util.h"
49
#include "pidref.h"
50
#include "process-util.h"
51
#include "rlimit-util.h"
52
#include "rm-rf.h"
53
#include "seccomp-util.h"
54
#include "securebits-util.h"
55
#include "serialize.h"
56
#include "set.h"
57
#include "sort-util.h"
58
#include "string-table.h"
59
#include "string-util.h"
60
#include "strv.h"
61
#include "syslog-util.h"
62
#include "terminal-util.h"
63
#include "tmpfile-util.h"
64
#include "utmp-wtmp.h"
65
#include "vpick.h"
66

67
const char* exec_context_tty_path(const ExecContext *context) {
12,961✔
68
        assert(context);
12,961✔
69

70
        if (context->stdio_as_fds)
12,961✔
71
                return NULL;
72

73
        if (context->tty_path)
12,136✔
74
                return context->tty_path;
487✔
75

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

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

85
        unsigned rows, cols;
1,056✔
86
        int r;
1,056✔
87

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

92
        if (!isatty_safe(output_fd))
1,056✔
93
                return 0;
1,056✔
94

95
        if (!tty_path)
528✔
96
                tty_path = exec_context_tty_path(context);
270✔
97

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

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

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

121
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
528✔
122
}
123

124
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
11,962✔
125
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
23,924✔
126
        int fd, r;
11,962✔
127

128
        assert(context);
11,962✔
129

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

135
        const char *path = exec_context_tty_path(context);
11,962✔
136

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

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

157
        if (context->tty_reset)
283✔
158
                (void) terminal_reset_defensive(
220✔
159
                                fd,
160
                                TERMINAL_RESET_SWITCH_TO_TEXT |
161
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
217✔
162

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

167
        if (!sd_id128_is_null(invocation_id) && exec_context_shall_ansi_seq_reset(context)) {
529✔
168
                sd_id128_t context_id;
23✔
169

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

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

184
        if (context->tty_vhangup)
283✔
185
                (void) terminal_vhangup_fd(fd);
97✔
186

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

191
        if (context->tty_vt_disallocate && path)
283✔
192
                (void) vt_disallocate(path);
49✔
193
}
194

195
bool exec_needs_network_namespace(const ExecContext *context) {
61,210✔
196
        assert(context);
61,210✔
197

198
        return context->private_network || context->network_namespace_path;
61,210✔
199
}
200

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

205
bool exec_needs_ipc_namespace(const ExecContext *context) {
55,783✔
206
        assert(context);
55,783✔
207

208
        return context->private_ipc || context->ipc_namespace_path;
55,783✔
209
}
210

211
static bool needs_cgroup_namespace(ProtectControlGroups i) {
134,193✔
212
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
134,193✔
213
}
214

215
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
83,256✔
216
        assert(context);
83,256✔
217

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

231
bool exec_needs_cgroup_namespace(const ExecContext *context) {
50,937✔
232
        assert(context);
50,937✔
233

234
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
50,937✔
235
}
236

237
bool exec_needs_cgroup_mount(const ExecContext *context) {
28,189✔
238
        assert(context);
28,189✔
239

240
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
28,189✔
241
}
242

243
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,066✔
244
        assert(context);
2,066✔
245

246
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,066✔
247
}
248

249
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
90,444✔
250
        assert(context);
90,444✔
251

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

256
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
81,133✔
257
}
258

259
bool exec_needs_mount_namespace(
33,353✔
260
                const ExecContext *context,
261
                const ExecParameters *params,
262
                const ExecRuntime *runtime) {
263

264
        assert(context);
33,353✔
265

266
        if (context->root_image)
33,353✔
267
                return true;
268

269
        if (context->root_directory_as_fd)
33,281✔
270
                return true;
271

272
        if (!strv_isempty(context->read_write_paths) ||
33,275✔
273
            !strv_isempty(context->read_only_paths) ||
31,000✔
274
            !strv_isempty(context->inaccessible_paths) ||
30,987✔
275
            !strv_isempty(context->exec_paths) ||
30,968✔
276
            !strv_isempty(context->no_exec_paths))
30,968✔
277
                return true;
278

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

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

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

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

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

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

297
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,680✔
298
                return true;
299

300
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
30,022✔
301
                return true;
302

303
        if (context->private_devices ||
29,492✔
304
            context->private_mounts > 0 ||
29,108✔
305
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,607✔
306
            context->protect_system != PROTECT_SYSTEM_NO ||
28,578✔
307
            context->protect_home != PROTECT_HOME_NO ||
28,578✔
308
            context->protect_kernel_tunables ||
28,578✔
309
            context->protect_kernel_modules ||
28,578✔
310
            context->protect_kernel_logs ||
53,729✔
311
            exec_needs_cgroup_mount(context) ||
26,863✔
312
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,845✔
313
            context->proc_subset != PROC_SUBSET_ALL ||
26,783✔
314
            context->private_bpf != PRIVATE_BPF_NO ||
53,548✔
315
            exec_needs_ipc_namespace(context) ||
53,548✔
316
            exec_needs_pid_namespace(context, params))
26,774✔
317
                return true;
2,759✔
318

319
        if (context->root_directory) {
26,733✔
320
                if (exec_context_get_effective_mount_apivfs(context))
5✔
321
                        return true;
322

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

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

332
        if (context->dynamic_user &&
26,728✔
333
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
334
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
335
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
336
                return true;
337

338
        if (exec_context_get_effective_bind_log_sockets(context))
26,728✔
339
                return true;
340

341
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
160,024✔
342
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
138,296✔
343
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
4,994✔
344
                                return true;
345

346
        return false;
347
}
348

349
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,979✔
350
        assert(context);
3,979✔
351
        assert(params);
3,979✔
352

353
        if (!params->notify_socket)
3,979✔
354
                return NULL;
355

356
        if (!needs_sandboxing)
3,262✔
357
                return NULL;
358

359
        if (!context->root_directory && !context->root_image && !context->root_directory_as_fd)
3,262✔
360
                return NULL;
361

362
        if (!exec_context_get_effective_mount_apivfs(context))
×
363
                return NULL;
364

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

368
        return "/run/host/notify";
369
}
370

371
int exec_log_level_max_with_exec_params(const ExecContext *context, const ExecParameters *params) {
20,494✔
372
        assert(params);
20,494✔
373

374
        if (params->debug_invocation)
20,494✔
375
                return LOG_DEBUG;
376

377
        return exec_log_level_max(context);
20,490✔
378
}
379

380
int exec_log_level_max(const ExecContext *context) {
20,536✔
381
        assert(context);
20,536✔
382
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
20,536✔
383
}
384

385
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
10,062✔
386
        assert(context);
10,062✔
387

388
        if (!context->dynamic_user)
10,062✔
389
                return false;
390

391
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
106✔
392
                return false;
393

394
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
100✔
395
                return false;
16✔
396

397
        return true;
398
}
399

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

405
int exec_params_get_cgroup_path(
10,961✔
406
                const ExecParameters *params,
407
                const CGroupContext *c,
408
                const char *prefix,
409
                char **ret) {
410

411
        const char *subgroup = NULL;
10,961✔
412
        char *p;
10,961✔
413

414
        assert(params);
10,961✔
415
        assert(c);
10,961✔
416
        assert(ret);
10,961✔
417

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

427
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
428
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
10,961✔
429
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
718✔
430
                        subgroup = ".control";
431
                else
432
                        subgroup = c->delegate_subgroup;
672✔
433
        }
434

435
        if (subgroup)
672✔
436
                p = path_join(prefix, subgroup);
718✔
437
        else
438
                p = strdup(strempty(prefix));
10,251✔
439
        if (!p)
10,961✔
440
                return -ENOMEM;
441

442
        *ret = p;
10,961✔
443
        return !!subgroup;
10,961✔
444
}
445

446
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
2,848✔
447
        assert(c);
2,848✔
448

449
        return c->cpu_affinity_from_numa;
2,848✔
450
}
451

452
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,563✔
453
        assert(unit);
2,563✔
454
        assert(msg);
2,563✔
455
        assert(executable);
2,563✔
456

457
        if (!DEBUG_LOGGING)
2,563✔
458
                return;
2,563✔
459

460
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
5,126✔
461

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

468
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret);
469

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

479
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,563✔
480
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
481
        _cleanup_fclose_ FILE *f = NULL;
2,563✔
482
        int r;
2,563✔
483

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

496
        LOG_CONTEXT_PUSH_UNIT(unit);
5,126✔
497

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

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

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

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

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

527
                cgtarget = subcgroup_path;
4✔
528
        } else
529
                cgtarget = params->cgroup_path;
2,559✔
530

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

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

542
        fdset = fdset_new();
2,563✔
543
        if (!fdset)
2,563✔
544
                return log_oom();
×
545

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

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

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

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

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

569
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,563✔
570
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,563✔
571

572
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,563✔
573
        dual_timestamp start_timestamp;
2,563✔
574

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

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

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

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

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

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

616
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,563✔
617

618
        *ret = TAKE_PIDREF(pidref);
2,563✔
619
        return 0;
2,563✔
620
}
621

622
void exec_context_init(ExecContext *c) {
62,817✔
623
        assert(c);
62,817✔
624

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

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

655
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
376,902✔
656
                d->mode = 0755;
314,085✔
657

658
        numa_policy_reset(&c->numa_policy);
62,817✔
659

660
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
62,817✔
661
}
62,817✔
662

663
void exec_context_done(ExecContext *c) {
52,605✔
664
        assert(c);
52,605✔
665

666
        c->environment = strv_free(c->environment);
52,605✔
667
        c->environment_files = strv_free(c->environment_files);
52,605✔
668
        c->pass_environment = strv_free(c->pass_environment);
52,605✔
669
        c->unset_environment = strv_free(c->unset_environment);
52,605✔
670

671
        rlimit_free_all(c->rlimit);
52,605✔
672

673
        for (size_t l = 0; l < 3; l++) {
210,420✔
674
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
157,815✔
675
                c->stdio_file[l] = mfree(c->stdio_file[l]);
157,815✔
676
        }
677

678
        c->working_directory = mfree(c->working_directory);
52,605✔
679
        c->root_directory = mfree(c->root_directory);
52,605✔
680
        c->root_image = mfree(c->root_image);
52,605✔
681
        c->root_image_options = mount_options_free_all(c->root_image_options);
52,605✔
682
        iovec_done(&c->root_hash);
52,605✔
683
        c->root_hash_path = mfree(c->root_hash_path);
52,605✔
684
        iovec_done(&c->root_hash_sig);
52,605✔
685
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
52,605✔
686
        c->root_verity = mfree(c->root_verity);
52,605✔
687
        c->tty_path = mfree(c->tty_path);
52,605✔
688
        c->syslog_identifier = mfree(c->syslog_identifier);
52,605✔
689
        c->user = mfree(c->user);
52,605✔
690
        c->group = mfree(c->group);
52,605✔
691

692
        c->supplementary_groups = strv_free(c->supplementary_groups);
52,605✔
693

694
        c->pam_name = mfree(c->pam_name);
52,605✔
695

696
        c->read_only_paths = strv_free(c->read_only_paths);
52,605✔
697
        c->read_write_paths = strv_free(c->read_write_paths);
52,605✔
698
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
52,605✔
699
        c->exec_paths = strv_free(c->exec_paths);
52,605✔
700
        c->no_exec_paths = strv_free(c->no_exec_paths);
52,605✔
701
        c->exec_search_path = strv_free(c->exec_search_path);
52,605✔
702

703
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
52,605✔
704
        c->bind_mounts = NULL;
52,605✔
705
        c->n_bind_mounts = 0;
52,605✔
706
        mount_image_free_many(c->mount_images, c->n_mount_images);
52,605✔
707
        c->mount_images = NULL;
52,605✔
708
        c->n_mount_images = 0;
52,605✔
709
        mount_image_free_many(c->extension_images, c->n_extension_images);
52,605✔
710
        c->extension_images = NULL;
52,605✔
711
        c->n_extension_images = 0;
52,605✔
712
        c->extension_directories = strv_free(c->extension_directories);
52,605✔
713
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
52,605✔
714
        c->temporary_filesystems = NULL;
52,605✔
715
        c->n_temporary_filesystems = 0;
52,605✔
716

717
        cpu_set_done(&c->cpu_set);
52,605✔
718
        numa_policy_reset(&c->numa_policy);
52,605✔
719

720
        c->utmp_id = mfree(c->utmp_id);
52,605✔
721
        c->selinux_context = mfree(c->selinux_context);
52,605✔
722
        c->apparmor_profile = mfree(c->apparmor_profile);
52,605✔
723
        c->smack_process_label = mfree(c->smack_process_label);
52,605✔
724

725
        c->restrict_filesystems = set_free(c->restrict_filesystems);
52,605✔
726

727
        c->syscall_filter = hashmap_free(c->syscall_filter);
52,605✔
728
        c->syscall_archs = set_free(c->syscall_archs);
52,605✔
729
        c->syscall_log = hashmap_free(c->syscall_log);
52,605✔
730
        c->address_families = set_free(c->address_families);
52,605✔
731

732
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
315,630✔
733
                exec_directory_done(d);
263,025✔
734

735
        c->log_level_max = -1;
52,605✔
736

737
        exec_context_free_log_extra_fields(c);
52,605✔
738
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
52,605✔
739
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
52,605✔
740

741
        c->log_ratelimit = (RateLimit) {};
52,605✔
742

743
        c->stdin_data = mfree(c->stdin_data);
52,605✔
744
        c->stdin_data_size = 0;
52,605✔
745

746
        c->user_namespace_path = mfree(c->user_namespace_path);
52,605✔
747
        c->network_namespace_path = mfree(c->network_namespace_path);
52,605✔
748
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
52,605✔
749

750
        c->log_namespace = mfree(c->log_namespace);
52,605✔
751

752
        c->load_credentials = hashmap_free(c->load_credentials);
52,605✔
753
        c->set_credentials = hashmap_free(c->set_credentials);
52,605✔
754
        c->import_credentials = ordered_set_free(c->import_credentials);
52,605✔
755

756
        c->root_image_policy = image_policy_free(c->root_image_policy);
52,605✔
757
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
52,605✔
758
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
52,605✔
759

760
        c->private_hostname = mfree(c->private_hostname);
52,605✔
761
}
52,605✔
762

763
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
6,244✔
764
        assert(c);
6,244✔
765

766
        if (!runtime_prefix)
6,244✔
767
                return 0;
768

769
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
6,247✔
770
                _cleanup_free_ char *p = NULL;
3✔
771

772
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
3✔
UNCOV
773
                        p = path_join(runtime_prefix, "private", i->path);
×
774
                else
775
                        p = path_join(runtime_prefix, i->path);
3✔
776
                if (!p)
3✔
777
                        return -ENOMEM;
778

779
                /* We execute this synchronously, since we need to be sure this is gone when we start the
780
                 * service next. */
781
                (void) rm_rf(p, REMOVE_ROOT);
3✔
782

783
                STRV_FOREACH(symlink, i->symlinks) {
3✔
UNCOV
784
                        _cleanup_free_ char *symlink_abs = NULL;
×
785

786
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
787
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
788
                        else
789
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
UNCOV
790
                        if (!symlink_abs)
×
UNCOV
791
                                return -ENOMEM;
×
792

UNCOV
793
                        (void) unlink(symlink_abs);
×
794
                }
795
        }
796

797
        return 0;
798
}
799

800
int exec_context_destroy_mount_ns_dir(Unit *u) {
12,787✔
801
        _cleanup_free_ char *p = NULL;
12,787✔
802

803
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
12,787✔
804
                return 0;
805

806
        p = path_join("/run/systemd/propagate/", u->id);
2,741✔
807
        if (!p)
2,741✔
808
                return -ENOMEM;
809

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

814
        return 0;
815
}
816

817
void exec_command_done(ExecCommand *c) {
110,503✔
818
        assert(c);
110,503✔
819

820
        c->path = mfree(c->path);
110,503✔
821
        c->argv = strv_free(c->argv);
110,503✔
822
}
110,503✔
823

824
void exec_command_done_array(ExecCommand *c, size_t n) {
30,834✔
825
        FOREACH_ARRAY(i, c, n)
123,334✔
826
                exec_command_done(i);
92,500✔
827
}
30,834✔
828

829
ExecCommand* exec_command_free(ExecCommand *c) {
17,969✔
830
        if (!c)
17,969✔
831
                return NULL;
832

833
        exec_command_done(c);
17,969✔
834
        return mfree(c);
17,969✔
835
}
836

837
ExecCommand* exec_command_free_list(ExecCommand *c) {
153,553✔
838
        ExecCommand *i;
153,553✔
839

840
        while ((i = LIST_POP(command, c)))
171,522✔
841
                exec_command_free(i);
17,969✔
842

843
        return NULL;
153,553✔
844
}
845

846
void exec_command_free_array(ExecCommand **c, size_t n) {
21,737✔
847
        FOREACH_ARRAY(i, c, n)
175,269✔
848
                *i = exec_command_free_list(*i);
153,532✔
849
}
21,737✔
850

851
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,531✔
852
        FOREACH_ARRAY(i, c, n)
30,123✔
853
                exec_status_reset(&i->exec_status);
22,592✔
854
}
7,531✔
855

856
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,539✔
857
        FOREACH_ARRAY(i, c, n)
39,786✔
858
                LIST_FOREACH(command, z, *i)
36,712✔
859
                        exec_status_reset(&z->exec_status);
2,465✔
860
}
5,539✔
861

862
typedef struct InvalidEnvInfo {
863
        const Unit *unit;
864
        const char *path;
865
} InvalidEnvInfo;
866

867
static void invalid_env(const char *p, void *userdata) {
×
UNCOV
868
        InvalidEnvInfo *info = userdata;
×
869

UNCOV
870
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
UNCOV
871
}
×
872

873
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,285✔
874
        assert(c);
39,285✔
875

876
        switch (fd_index) {
39,285✔
877

878
        case STDIN_FILENO:
13,095✔
879
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,095✔
880
                        return NULL;
881

UNCOV
882
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
883

884
        case STDOUT_FILENO:
13,095✔
885
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,095✔
886
                        return NULL;
887

UNCOV
888
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
889

890
        case STDERR_FILENO:
13,095✔
891
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,095✔
892
                        return NULL;
893

UNCOV
894
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
895

896
        default:
897
                return NULL;
898
        }
899
}
900

901
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,563✔
902
        _cleanup_strv_free_ char **v = NULL;
2,563✔
903
        int r;
2,563✔
904

905
        assert(c);
2,563✔
906
        assert(ret);
2,563✔
907

908
        STRV_FOREACH(i, c->environment_files) {
2,566✔
909
                _cleanup_strv_free_ char **paths = NULL;
3✔
910
                bool ignore = false;
3✔
911
                char *fn = *i;
3✔
912

913
                if (fn[0] == '-') {
3✔
914
                        ignore = true;
2✔
915
                        fn++;
2✔
916
                }
917

918
                if (!path_is_absolute(fn)) {
3✔
UNCOV
919
                        if (ignore)
×
UNCOV
920
                                continue;
×
921
                        return -EINVAL;
922
                }
923

924
                /* Filename supports globbing, take all matching files */
925
                r = safe_glob(fn, /* flags= */ 0, &paths);
3✔
926
                if (r < 0) {
3✔
927
                        if (ignore)
2✔
928
                                continue;
2✔
929
                        return r;
930
                }
931

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

935
                STRV_FOREACH(path, paths) {
2✔
936
                        _cleanup_strv_free_ char **p = NULL;
1✔
937

938
                        r = load_env_file(NULL, *path, &p);
1✔
939
                        if (r < 0) {
1✔
UNCOV
940
                                if (ignore)
×
UNCOV
941
                                        continue;
×
942
                                return r;
943
                        }
944

945
                        /* Log invalid environment variables with filename */
946
                        if (p) {
1✔
947
                                InvalidEnvInfo info = {
1✔
948
                                        .unit = unit,
949
                                        .path = *path,
1✔
950
                                };
951

952
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
953
                        }
954

955
                        if (!v)
1✔
956
                                v = TAKE_PTR(p);
1✔
957
                        else {
958
                                char **m = strv_env_merge(v, p);
×
UNCOV
959
                                if (!m)
×
UNCOV
960
                                        return -ENOMEM;
×
961

UNCOV
962
                                strv_free_and_replace(v, m);
×
963
                        }
964
                }
965
        }
966

967
        *ret = TAKE_PTR(v);
2,563✔
968

969
        return 0;
2,563✔
970
}
971

972
static bool tty_may_match_dev_console(const char *tty) {
247✔
973
        _cleanup_free_ char *resolved = NULL;
247✔
974

975
        if (!tty)
247✔
976
                return true;
977

978
        tty = skip_dev_prefix(tty);
247✔
979

980
        /* trivial identity? */
981
        if (streq(tty, "console"))
247✔
982
                return true;
983

984
        if (resolve_dev_console(&resolved) < 0)
37✔
985
                return true; /* if we could not resolve, assume it may */
986

987
        /* "tty0" means the active VC, so it may be the same sometimes */
988
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
37✔
989
}
990

991
static bool exec_context_may_touch_tty(const ExecContext *ec) {
25,924✔
992
        assert(ec);
25,924✔
993

994
        return ec->tty_reset ||
51,613✔
995
                ec->tty_vhangup ||
25,689✔
996
                ec->tty_vt_disallocate ||
25,689✔
997
                exec_input_is_terminal(ec->std_input) ||
25,689✔
998
                ec->std_output == EXEC_OUTPUT_TTY ||
51,613✔
999
                ec->std_error == EXEC_OUTPUT_TTY;
25,640✔
1000
}
1001

1002
bool exec_context_may_touch_console(const ExecContext *ec) {
24,208✔
1003

1004
        return exec_context_may_touch_tty(ec) &&
24,455✔
1005
               tty_may_match_dev_console(exec_context_tty_path(ec));
247✔
1006
}
1007

1008
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,172✔
1009
        assert(c);
1,172✔
1010

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

1018
        if (!c->tty_reset)
1,172✔
1019
                return false;
1020

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

1027
static void strv_fprintf(FILE *f, char **l) {
2✔
1028
        assert(f);
2✔
1029

1030
        STRV_FOREACH(g, l)
6✔
1031
                fprintf(f, " %s", *g);
4✔
1032
}
2✔
1033

1034
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
3,096✔
1035
        assert(f);
3,096✔
1036
        assert(prefix);
3,096✔
1037
        assert(name);
3,096✔
1038

1039
        if (!strv_isempty(strv)) {
3,096✔
1040
                fprintf(f, "%s%s:", prefix, name);
2✔
1041
                strv_fprintf(f, strv);
2✔
1042
                fputs("\n", f);
2✔
1043
        }
1044
}
3,096✔
1045

1046
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
UNCOV
1047
        assert(p);
×
1048
        assert(f);
×
1049

UNCOV
1050
        prefix = strempty(prefix);
×
1051

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

1078
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1079
        strv_dump(f, prefix, "Environment", p->environment);
×
UNCOV
1080
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1081

1082
        LIST_FOREACH(open_files, file, p->open_files)
×
UNCOV
1083
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1084

UNCOV
1085
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
UNCOV
1086
}
×
1087

1088
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
387✔
1089
        int r;
387✔
1090

1091
        assert(c);
387✔
1092
        assert(f);
387✔
1093

1094
        prefix = strempty(prefix);
387✔
1095

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

1156
        if (c->private_bpf == PRIVATE_BPF_YES) {
387✔
1157
                _cleanup_free_ char
×
UNCOV
1158
                        *commands = bpf_delegate_commands_to_string(c->bpf_delegate_commands),
×
1159
                        *maps = bpf_delegate_maps_to_string(c->bpf_delegate_maps),
×
1160
                        *programs = bpf_delegate_programs_to_string(c->bpf_delegate_programs),
×
1161
                        *attachments = bpf_delegate_attachments_to_string(c->bpf_delegate_attachments);
×
1162

UNCOV
1163
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
UNCOV
1164
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
UNCOV
1165
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
UNCOV
1166
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1167
        }
1168

1169
        if (c->set_login_environment >= 0)
387✔
1170
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1171

1172
        if (c->root_image)
387✔
UNCOV
1173
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1174

1175
        if (c->root_image_options) {
387✔
UNCOV
1176
                _cleanup_free_ char *opts_str = NULL;
×
1177

UNCOV
1178
                if (mount_options_to_string(c->root_image_options, &opts_str) >= 0 && !isempty(opts_str))
×
1179
                        fprintf(f, "%sRootImageOptions: %s\n", prefix, opts_str);
×
1180
        }
1181

1182
        if (iovec_is_set(&c->root_hash)) {
387✔
UNCOV
1183
                _cleanup_free_ char *encoded = NULL;
×
UNCOV
1184
                encoded = hexmem(c->root_hash.iov_base, c->root_hash.iov_len);
×
UNCOV
1185
                if (encoded)
×
1186
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1187
        }
1188

1189
        if (c->root_hash_path)
387✔
1190
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1191

1192
        if (iovec_is_set(&c->root_hash_sig)) {
1193
                _cleanup_free_ char *encoded = NULL;
×
UNCOV
1194
                ssize_t len;
×
UNCOV
1195
                len = base64mem(c->root_hash_sig.iov_base, c->root_hash_sig.iov_len, &encoded);
×
UNCOV
1196
                if (len)
×
1197
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1198
        }
1199

1200
        if (c->root_hash_sig_path)
387✔
UNCOV
1201
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1202

1203
        if (c->root_verity)
387✔
UNCOV
1204
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1205

1206
        STRV_FOREACH(e, c->environment)
393✔
1207
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
6✔
1208

1209
        STRV_FOREACH(e, c->environment_files)
387✔
UNCOV
1210
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1211

1212
        STRV_FOREACH(e, c->pass_environment)
399✔
1213
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
12✔
1214

1215
        STRV_FOREACH(e, c->unset_environment)
402✔
1216
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
15✔
1217

1218
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
387✔
1219

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

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

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

1236
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
387✔
1237

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

1241
        if (c->nice_set)
387✔
1242
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
2✔
1243

1244
        if (c->oom_score_adjust_set)
387✔
1245
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
16✔
1246

1247
        if (c->coredump_filter_set)
387✔
UNCOV
1248
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1249

1250
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
6,579✔
1251
                if (c->rlimit[i]) {
6,192✔
1252
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
350✔
1253
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1254
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
350✔
1255
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
350✔
1256
                }
1257

1258
        if (c->ioprio_is_set) {
387✔
1259
                _cleanup_free_ char *class_str = NULL;
3✔
1260

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

1265
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
3✔
1266
        }
1267

1268
        if (c->cpu_sched_set) {
387✔
1269
                _cleanup_free_ char *policy_str = NULL;
×
1270

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

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

1282
        if (c->cpu_set.set) {
387✔
UNCOV
1283
                _cleanup_free_ char *affinity = NULL;
×
1284

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

1289
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
387✔
1290
                _cleanup_free_ char *nodes = NULL;
1✔
1291

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

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

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

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

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

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

1345
        if (IN_SET(c->std_output,
387✔
1346
                   EXEC_OUTPUT_KMSG,
1347
                   EXEC_OUTPUT_JOURNAL,
1348
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1349
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1350
            IN_SET(c->std_error,
24✔
1351
                   EXEC_OUTPUT_KMSG,
1352
                   EXEC_OUTPUT_JOURNAL,
1353
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1354
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1355

1356
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
363✔
1357

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

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

1367
        if (c->log_level_max >= 0) {
387✔
1368
                _cleanup_free_ char *t = NULL;
1✔
1369

1370
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1371

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

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

1380
        if (c->log_ratelimit.burst > 0)
387✔
UNCOV
1381
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1382

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

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

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

1400
        if (c->log_namespace)
387✔
UNCOV
1401
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1402

1403
        if (c->secure_bits) {
387✔
1404
                _cleanup_free_ char *str = NULL;
×
1405

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

1411
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
387✔
1412
                _cleanup_free_ char *str = NULL;
387✔
1413

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

1419
        if (c->capability_ambient_set != 0) {
387✔
1420
                _cleanup_free_ char *str = NULL;
3✔
1421

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

1427
        if (c->user)
387✔
1428
                fprintf(f, "%sUser: %s\n", prefix, c->user);
7✔
1429
        if (c->group)
387✔
1430
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1431

1432
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
773✔
1433

1434
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
387✔
1435

1436
        if (c->pam_name)
387✔
1437
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
1✔
1438

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

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

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

1460
        if (c->utmp_id)
387✔
1461
                fprintf(f,
2✔
1462
                        "%sUtmpIdentifier: %s\n",
1463
                        prefix, c->utmp_id);
1464

1465
        if (c->selinux_context)
387✔
UNCOV
1466
                fprintf(f,
×
1467
                        "%sSELinuxContext: %s%s\n",
UNCOV
1468
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1469

1470
        if (c->apparmor_profile)
387✔
UNCOV
1471
                fprintf(f,
×
1472
                        "%sAppArmorProfile: %s%s\n",
UNCOV
1473
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1474

1475
        if (c->smack_process_label)
387✔
UNCOV
1476
                fprintf(f,
×
1477
                        "%sSmackProcessLabel: %s%s\n",
UNCOV
1478
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1479

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

1485
        fprintf(f,
387✔
1486
                "%sLockPersonality: %s\n",
1487
                prefix, yes_no(c->lock_personality));
387✔
1488

1489
        if (c->syscall_filter) {
387✔
1490
                fprintf(f,
24✔
1491
                        "%sSystemCallFilter: ",
1492
                        prefix);
1493

1494
                if (!c->syscall_allow_list)
24✔
UNCOV
1495
                        fputc('~', f);
×
1496

1497
#if HAVE_SECCOMP
1498
                if (dlopen_libseccomp() >= 0) {
24✔
1499
                        void *id, *val;
24✔
1500
                        bool first = true;
24✔
1501
                        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
9,356✔
1502
                                _cleanup_free_ char *name = NULL;
9,332✔
1503
                                const char *errno_name = NULL;
9,332✔
1504
                                int num = PTR_TO_INT(val);
9,332✔
1505

1506
                                if (first)
9,332✔
1507
                                        first = false;
1508
                                else
1509
                                        fputc(' ', f);
9,308✔
1510

1511
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
9,332✔
1512
                                fputs(strna(name), f);
9,332✔
1513

1514
                                if (num >= 0) {
9,332✔
1515
                                        errno_name = seccomp_errno_or_action_to_string(num);
×
UNCOV
1516
                                        if (errno_name)
×
UNCOV
1517
                                                fprintf(f, ":%s", errno_name);
×
1518
                                        else
UNCOV
1519
                                                fprintf(f, ":%d", num);
×
1520
                                }
1521
                        }
1522
                }
1523
#endif
1524

1525
                fputc('\n', f);
24✔
1526
        }
1527

1528
        if (c->syscall_archs) {
387✔
1529
                fprintf(f,
26✔
1530
                        "%sSystemCallArchitectures:",
1531
                        prefix);
1532

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

1541
        if (exec_context_restrict_namespaces_set(c)) {
387✔
1542
                _cleanup_free_ char *s = NULL;
22✔
1543

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

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

1558
        if (c->user_namespace_path)
387✔
UNCOV
1559
                fprintf(f,
×
1560
                        "%sUserNamespacePath: %s\n",
1561
                        prefix, c->user_namespace_path);
1562

1563
        if (c->network_namespace_path)
387✔
UNCOV
1564
                fprintf(f,
×
1565
                        "%sNetworkNamespacePath: %s\n",
1566
                        prefix, c->network_namespace_path);
1567

1568
        if (c->syscall_errno > 0) {
387✔
1569
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
386✔
1570

1571
#if HAVE_SECCOMP
1572
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
386✔
1573
                if (errno_name)
386✔
1574
                        fputs(errno_name, f);
386✔
1575
                else
UNCOV
1576
                        fprintf(f, "%d", c->syscall_errno);
×
1577
#endif
1578
                fputc('\n', f);
386✔
1579
        }
1580

1581
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
387✔
1582
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1583
                        mount->ignore_enoent ? "-": "",
×
1584
                        mount->source,
1585
                        mount->destination);
1586
                if (mount->mount_options) {
×
UNCOV
1587
                        _cleanup_free_ char *opts = NULL;
×
1588

UNCOV
1589
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
UNCOV
1590
                                fprintf(f, " %s", opts);
×
1591
                }
1592
                fprintf(f, "\n");
×
1593
        }
1594

1595
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
387✔
1596
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
UNCOV
1597
                        mount->ignore_enoent ? "-": "",
×
1598
                        mount->source);
1599
                if (mount->mount_options) {
×
UNCOV
1600
                        _cleanup_free_ char *opts = NULL;
×
1601

UNCOV
1602
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
UNCOV
1603
                                fprintf(f, " %s", opts);
×
1604
                }
UNCOV
1605
                fprintf(f, "\n");
×
1606
        }
1607

1608
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
387✔
1609
}
387✔
1610

UNCOV
1611
bool exec_context_maintains_privileges(const ExecContext *c) {
×
UNCOV
1612
        assert(c);
×
1613

1614
        /* Returns true if the process forked off would run under
1615
         * an unchanged UID or as root. */
1616

1617
        if (!c->user)
×
1618
                return true;
1619

UNCOV
1620
        if (STR_IN_SET(c->user, "root", "0"))
×
UNCOV
1621
                return true;
×
1622

UNCOV
1623
        return false;
×
1624
}
1625

1626
int exec_context_get_effective_ioprio(const ExecContext *c) {
5,696✔
1627
        int p;
5,696✔
1628

1629
        assert(c);
5,696✔
1630

1631
        if (c->ioprio_is_set)
5,696✔
1632
                return c->ioprio;
14✔
1633

1634
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
5,682✔
1635
        if (p < 0)
5,682✔
1636
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1637

1638
        return ioprio_normalize(p);
5,682✔
1639
}
1640

1641
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
39,385✔
1642
        assert(c);
39,385✔
1643

1644
        /* Explicit setting wins */
1645
        if (c->mount_apivfs >= 0)
39,385✔
1646
                return c->mount_apivfs > 0;
119✔
1647

1648
        /* Default to "yes" if root directory or image are specified */
1649
        if (exec_context_with_rootfs(c))
39,266✔
1650
                return true;
188✔
1651

1652
        return false;
1653
}
1654

1655
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
32,027✔
1656
        assert(c);
32,027✔
1657

1658
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1659
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1660
        if (c->log_namespace)
32,027✔
1661
                return true;
1662

1663
        if (c->bind_log_sockets >= 0)
32,019✔
1664
                return c->bind_log_sockets > 0;
2✔
1665

1666
        if (exec_context_get_effective_mount_apivfs(c))
32,017✔
1667
                return true;
1668

1669
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1670
        if (exec_context_with_rootfs(c) && c->private_devices)
31,886✔
UNCOV
1671
                return true;
×
1672

1673
        return false;
1674
}
1675

1676
void exec_context_free_log_extra_fields(ExecContext *c) {
52,607✔
1677
        assert(c);
52,607✔
1678

1679
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
52,858✔
1680
                free(field->iov_base);
251✔
1681

1682
        c->log_extra_fields = mfree(c->log_extra_fields);
52,607✔
1683
        c->n_log_extra_fields = 0;
52,607✔
1684
}
52,607✔
1685

1686
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,716✔
1687
        _cleanup_close_ int fd = -EBADF;
1,716✔
1688
        const char *path;
1,716✔
1689
        struct stat st;
1,716✔
1690
        int r;
1,716✔
1691

1692
        assert(c);
1,716✔
1693

1694
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1695
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,716✔
1696

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

1703
        path = exec_context_tty_path(c);
37✔
1704
        if (!path)
37✔
1705
                return;
1706

1707
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
37✔
1708
        if (fd < 0)
37✔
UNCOV
1709
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1710
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1711
                                             path);
1712

1713
        if (fstat(fd, &st) < 0)
37✔
UNCOV
1714
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1715

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

1724
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
37✔
1725
        if (r < 0)
37✔
1726
                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);
37✔
1727
}
1728

1729
int exec_context_get_clean_directories(
1✔
1730
                ExecContext *c,
1731
                char **prefix,
1732
                ExecCleanMask mask,
1733
                char ***ret) {
1734

1735
        _cleanup_strv_free_ char **l = NULL;
1✔
1736
        int r;
1✔
1737

1738
        assert(c);
1✔
1739
        assert(prefix);
1✔
1740
        assert(ret);
1✔
1741

1742
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
6✔
1743
                if (!BIT_SET(mask, t))
5✔
UNCOV
1744
                        continue;
×
1745

1746
                if (!prefix[t])
5✔
UNCOV
1747
                        continue;
×
1748

1749
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
7✔
1750
                        char *j;
2✔
1751

1752
                        j = path_join(prefix[t], i->path);
2✔
1753
                        if (!j)
2✔
1754
                                return -ENOMEM;
1755

1756
                        r = strv_consume(&l, j);
2✔
1757
                        if (r < 0)
2✔
1758
                                return r;
1759

1760
                        /* Also remove private directories unconditionally. */
1761
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
2✔
1762
                                j = path_join(prefix[t], "private", i->path);
2✔
1763
                                if (!j)
2✔
1764
                                        return -ENOMEM;
1765

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

1771
                        STRV_FOREACH(symlink, i->symlinks) {
2✔
1772
                                j = path_join(prefix[t], *symlink);
×
1773
                                if (!j)
×
1774
                                        return -ENOMEM;
1775

UNCOV
1776
                                r = strv_consume(&l, j);
×
UNCOV
1777
                                if (r < 0)
×
1778
                                        return r;
1779
                        }
1780
                }
1781
        }
1782

1783
        *ret = TAKE_PTR(l);
1✔
1784
        return 0;
1✔
1785
}
1786

1787
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
2,832✔
1788
        ExecCleanMask mask = 0;
2,832✔
1789

1790
        assert(c);
2,832✔
1791
        assert(ret);
2,832✔
1792

1793
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
16,992✔
1794
                if (c->directories[t].n_items > 0)
14,160✔
1795
                        mask |= 1U << t;
263✔
1796

1797
        *ret = mask;
2,832✔
1798
        return 0;
2,832✔
1799
}
1800

1801
int exec_context_get_oom_score_adjust(const ExecContext *c) {
2,848✔
1802
        int n = 0, r;
2,848✔
1803

1804
        assert(c);
2,848✔
1805

1806
        if (c->oom_score_adjust_set)
2,848✔
1807
                return c->oom_score_adjust;
585✔
1808

1809
        r = get_oom_score_adjust(&n);
2,263✔
1810
        if (r < 0)
2,263✔
UNCOV
1811
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1812

1813
        return n;
2,263✔
1814
}
1815

1816
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
2,848✔
1817
        _cleanup_free_ char *t = NULL;
2,848✔
1818
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
2,848✔
1819
        int r;
2,848✔
1820

1821
        assert(c);
2,848✔
1822

1823
        if (c->coredump_filter_set)
2,848✔
1824
                return c->coredump_filter;
×
1825

1826
        r = read_one_line_file("/proc/self/coredump_filter", &t);
2,848✔
1827
        if (r < 0)
2,848✔
1828
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1829
        else {
1830
                r = safe_atoux64(t, &n);
2,848✔
1831
                if (r < 0)
2,848✔
UNCOV
1832
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1833
        }
1834

1835
        return n;
2,848✔
1836
}
1837

1838
int exec_context_get_nice(const ExecContext *c) {
2,848✔
1839
        int n;
2,848✔
1840

1841
        assert(c);
2,848✔
1842

1843
        if (c->nice_set)
2,848✔
1844
                return c->nice;
8✔
1845

1846
        errno = 0;
2,840✔
1847
        n = getpriority(PRIO_PROCESS, 0);
2,840✔
1848
        if (errno > 0) {
2,840✔
UNCOV
1849
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1850
                n = 0;
1851
        }
1852

1853
        return n;
1854
}
1855

1856
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
2,848✔
1857
        int n;
2,848✔
1858

1859
        assert(c);
2,848✔
1860

1861
        if (c->cpu_sched_set)
2,848✔
1862
                return c->cpu_sched_policy;
×
1863

1864
        n = sched_getscheduler(0);
2,848✔
1865
        if (n < 0)
2,848✔
UNCOV
1866
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1867

1868
        return n < 0 ? SCHED_OTHER : n;
2,848✔
1869
}
1870

1871
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
2,848✔
1872
        struct sched_param p = {};
2,848✔
1873
        int r;
2,848✔
1874

1875
        assert(c);
2,848✔
1876

1877
        if (c->cpu_sched_set)
2,848✔
1878
                return c->cpu_sched_priority;
×
1879

1880
        r = sched_getparam(0, &p);
2,848✔
1881
        if (r < 0)
2,848✔
UNCOV
1882
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1883

1884
        return r >= 0 ? p.sched_priority : 0;
2,848✔
1885
}
1886

1887
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
2,848✔
1888
        int r;
2,848✔
1889

1890
        assert(c);
2,848✔
1891

1892
        if (c->timer_slack_nsec != NSEC_INFINITY)
2,848✔
1893
                return c->timer_slack_nsec;
1894

1895
        r = prctl(PR_GET_TIMERSLACK);
2,848✔
1896
        if (r < 0)
2,848✔
UNCOV
1897
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1898

1899
        return (uint64_t) MAX(r, 0);
2,848✔
1900
}
1901

1902
bool exec_context_get_set_login_environment(const ExecContext *c) {
13,043✔
1903
        assert(c);
13,043✔
1904

1905
        if (c->set_login_environment >= 0)
13,043✔
UNCOV
1906
                return c->set_login_environment;
×
1907

1908
        return c->user || c->dynamic_user || c->pam_name;
22,688✔
1909
}
1910

1911
char** exec_context_get_syscall_filter(const ExecContext *c) {
2,848✔
1912
        _cleanup_strv_free_ char **l = NULL;
2,848✔
1913

1914
        assert(c);
2,848✔
1915

1916
#if HAVE_SECCOMP
1917
        if (dlopen_libseccomp() < 0)
2,848✔
UNCOV
1918
                return strv_new(NULL);
×
1919

1920
        void *id, *val;
2,848✔
1921
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
15,409✔
1922
                _cleanup_free_ char *name = NULL;
12,561✔
1923
                const char *e = NULL;
12,561✔
1924
                char *s;
12,561✔
1925
                int num = PTR_TO_INT(val);
12,561✔
1926

1927
                if (c->syscall_allow_list && num >= 0)
12,561✔
1928
                        /* syscall with num >= 0 in allow-list is denied. */
1929
                        continue;
×
1930

1931
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
12,561✔
1932
                if (!name)
12,561✔
1933
                        continue;
×
1934

1935
                if (num >= 0) {
12,561✔
UNCOV
1936
                        e = seccomp_errno_or_action_to_string(num);
×
UNCOV
1937
                        if (e) {
×
1938
                                s = strjoin(name, ":", e);
×
UNCOV
1939
                                if (!s)
×
1940
                                        return NULL;
1941
                        } else {
UNCOV
1942
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1943
                                        return NULL;
1944
                        }
1945
                } else
1946
                        s = TAKE_PTR(name);
12,561✔
1947

1948
                if (strv_consume(&l, s) < 0)
12,561✔
1949
                        return NULL;
1950
        }
1951

1952
        strv_sort(l);
2,848✔
1953
#endif
1954

1955
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,848✔
1956
}
1957

1958
char** exec_context_get_syscall_archs(const ExecContext *c) {
2,848✔
1959
        _cleanup_strv_free_ char **l = NULL;
2,848✔
1960

1961
        assert(c);
2,848✔
1962

1963
#if HAVE_SECCOMP
1964
        void *id;
2,848✔
1965
        SET_FOREACH(id, c->syscall_archs) {
2,884✔
1966
                const char *name;
36✔
1967

1968
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
36✔
1969
                if (!name)
36✔
UNCOV
1970
                        continue;
×
1971

1972
                if (strv_extend(&l, name) < 0)
36✔
UNCOV
1973
                        return NULL;
×
1974
        }
1975

1976
        strv_sort(l);
2,848✔
1977
#endif
1978

1979
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,848✔
1980
}
1981

1982
char** exec_context_get_syscall_log(const ExecContext *c) {
2,848✔
1983
        _cleanup_strv_free_ char **l = NULL;
2,848✔
1984

1985
        assert(c);
2,848✔
1986

1987
#if HAVE_SECCOMP
1988
        if (dlopen_libseccomp() < 0)
2,848✔
1989
                return strv_new(NULL);
×
1990

1991
        void *id, *val;
2,848✔
1992
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
2,848✔
1993
                char *name = NULL;
×
1994

1995
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1996
                if (!name)
×
UNCOV
1997
                        continue;
×
1998

UNCOV
1999
                if (strv_consume(&l, name) < 0)
×
UNCOV
2000
                        return NULL;
×
2001
        }
2002

2003
        strv_sort(l);
2,848✔
2004
#endif
2005

2006
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,848✔
2007
}
2008

2009
char** exec_context_get_address_families(const ExecContext *c) {
2,848✔
2010
        _cleanup_strv_free_ char **l = NULL;
2,848✔
2011
        void *af;
2,848✔
2012

2013
        assert(c);
2,848✔
2014

2015
        SET_FOREACH(af, c->address_families) {
2,966✔
2016
                const char *name;
118✔
2017

2018
                name = af_to_name(PTR_TO_INT(af));
118✔
2019
                if (!name)
118✔
UNCOV
2020
                        continue;
×
2021

2022
                if (strv_extend(&l, name) < 0)
118✔
UNCOV
2023
                        return NULL;
×
2024
        }
2025

2026
        strv_sort(l);
2,848✔
2027

2028
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,848✔
2029
}
2030

2031
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
2,848✔
2032
        assert(c);
2,848✔
2033

2034
#if HAVE_LIBBPF
2035
        char **l = set_get_strv(c->restrict_filesystems);
2,848✔
2036
        if (!l)
2,848✔
2037
                return NULL;
2038

2039
        return strv_sort(l);
2,848✔
2040
#else
2041
        return strv_new(NULL);
2042
#endif
2043
}
2044

2045
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,759✔
2046
        assert(c);
13,759✔
2047

2048
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,759✔
2049
}
2050

2051
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
15,720✔
2052
        assert(c);
15,720✔
2053

2054
        return c->restrict_filesystems_allow_list ||
15,720✔
2055
          !set_isempty(c->restrict_filesystems);
15,720✔
2056
}
2057

2058
bool exec_context_with_rootfs(const ExecContext *c) {
71,435✔
2059
        assert(c);
71,435✔
2060

2061
        /* Checks if RootDirectory=, RootImage= or RootDirectoryFileDescriptor= are used */
2062

2063
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd;
71,435✔
2064
}
2065

2066
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2067
        int r;
4✔
2068

2069
        assert(context);
4✔
2070

2071
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
4✔
UNCOV
2072
                r = path_uses_vpick(mi->source);
×
2073
                if (r != 0)
×
2074
                        return r;
2075
        }
2076
        STRV_FOREACH(ed, context->extension_directories) {
4✔
UNCOV
2077
                r = path_uses_vpick(*ed);
×
UNCOV
2078
                if (r != 0)
×
2079
                        return r;
2080
        }
2081

2082
        return 0;
2083
}
2084

2085
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
5,095✔
2086
        assert(s);
5,095✔
2087

2088
        *s = (ExecStatus) {
5,095✔
2089
                .pid = pid,
2090
        };
2091

2092
        if (ts)
5,095✔
2093
                s->start_timestamp = *ts;
5,095✔
2094
        else
UNCOV
2095
                dual_timestamp_now(&s->start_timestamp);
×
2096
}
5,095✔
2097

2098
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,370✔
2099
        assert(s);
2,370✔
2100

2101
        if (s->pid != pid)
2,370✔
2102
                *s = (ExecStatus) {
5✔
2103
                        .pid = pid,
2104
                };
2105

2106
        dual_timestamp_now(&s->exit_timestamp);
2,370✔
2107

2108
        s->code = code;
2,370✔
2109
        s->status = status;
2,370✔
2110

2111
        if (context && context->utmp_id)
2,370✔
2112
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2113
}
2,370✔
2114

2115
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
8,742✔
2116
        assert(s);
8,742✔
2117
        assert(ucred);
8,742✔
2118
        assert(ts);
8,742✔
2119

2120
        if (ucred->pid != s->pid)
8,742✔
2121
                *s = (ExecStatus) {
7✔
2122
                        .pid = ucred->pid,
2123
                };
2124

2125
        s->handoff_timestamp = *ts;
8,742✔
2126
}
8,742✔
2127

2128
void exec_status_reset(ExecStatus *s) {
27,241✔
2129
        assert(s);
27,241✔
2130

2131
        *s = (ExecStatus) {};
27,241✔
2132
}
27,241✔
2133

2134
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
225✔
2135
        assert(s);
225✔
2136
        assert(f);
225✔
2137

2138
        if (s->pid <= 0)
225✔
2139
                return;
2140

2141
        prefix = strempty(prefix);
41✔
2142

2143
        fprintf(f,
41✔
2144
                "%sPID: "PID_FMT"\n",
2145
                prefix, s->pid);
2146

2147
        if (dual_timestamp_is_set(&s->start_timestamp))
41✔
2148
                fprintf(f,
41✔
2149
                        "%sStart Timestamp: %s\n",
2150
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
41✔
2151

2152
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
41✔
2153
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
40✔
2154
                fprintf(f,
40✔
2155
                        "%sHandoff Timestamp: %s since start\n",
2156
                        prefix,
2157
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
80✔
2158
        else
2159
                fprintf(f,
1✔
2160
                        "%sHandoff Timestamp: %s\n",
2161
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
1✔
2162

2163
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
41✔
2164

2165
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
19✔
2166
                        fprintf(f,
19✔
2167
                                "%sExit Timestamp: %s since handoff\n",
2168
                                prefix,
2169
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
38✔
2170
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
UNCOV
2171
                        fprintf(f,
×
2172
                                "%sExit Timestamp: %s since start\n",
2173
                                prefix,
2174
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2175
                else
UNCOV
2176
                        fprintf(f,
×
2177
                                "%sExit Timestamp: %s\n",
UNCOV
2178
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2179

2180
                fprintf(f,
19✔
2181
                        "%sExit Code: %s\n"
2182
                        "%sExit Status: %i\n",
2183
                        prefix, sigchld_code_to_string(s->code),
19✔
2184
                        prefix, s->status);
19✔
2185
        }
2186
}
2187

2188
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
225✔
2189
        _cleanup_free_ char *cmd = NULL;
450✔
2190
        const char *prefix2;
225✔
2191

2192
        assert(c);
225✔
2193
        assert(f);
225✔
2194

2195
        prefix = strempty(prefix);
225✔
2196
        prefix2 = strjoina(prefix, "\t");
1,125✔
2197

2198
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
225✔
2199

2200
        fprintf(f,
225✔
2201
                "%sCommand Line: %s\n",
2202
                prefix, strnull(cmd));
2203

2204
        exec_status_dump(&c->exec_status, f, prefix2);
225✔
2205
}
225✔
2206

2207
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
220✔
2208
        assert(f);
220✔
2209

2210
        prefix = strempty(prefix);
220✔
2211

2212
        LIST_FOREACH(command, i, c)
444✔
2213
                exec_command_dump(i, f, prefix);
224✔
2214
}
220✔
2215

2216
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
17,969✔
2217
        ExecCommand *end;
17,969✔
2218

2219
        assert(l);
17,969✔
2220
        assert(e);
17,969✔
2221

2222
        if (*l) {
17,969✔
2223
                /* It's kind of important, that we keep the order here */
2224
                end = LIST_FIND_TAIL(command, *l);
373✔
2225
                LIST_INSERT_AFTER(command, *l, end, e);
136✔
2226
        } else
2227
                *l = e;
17,833✔
2228
}
17,969✔
2229

2230
int exec_command_set(ExecCommand *c, const char *path, ...) {
233✔
2231
        va_list ap;
233✔
2232
        char **l, *p;
233✔
2233

2234
        assert(c);
233✔
2235
        assert(path);
233✔
2236

2237
        va_start(ap, path);
233✔
2238
        l = strv_new_ap(path, ap);
233✔
2239
        va_end(ap);
233✔
2240

2241
        if (!l)
233✔
2242
                return -ENOMEM;
233✔
2243

2244
        p = strdup(path);
233✔
2245
        if (!p) {
233✔
UNCOV
2246
                strv_free(l);
×
UNCOV
2247
                return -ENOMEM;
×
2248
        }
2249

2250
        free_and_replace(c->path, p);
233✔
2251

2252
        return strv_free_and_replace(c->argv, l);
233✔
2253
}
2254

2255
int exec_command_append(ExecCommand *c, const char *path, ...) {
340✔
2256
        char **l;
340✔
2257
        va_list ap;
340✔
2258
        int r;
340✔
2259

2260
        assert(c);
340✔
2261
        assert(path);
340✔
2262

2263
        va_start(ap, path);
340✔
2264
        l = strv_new_ap(path, ap);
340✔
2265
        va_end(ap);
340✔
2266

2267
        if (!l)
340✔
2268
                return -ENOMEM;
340✔
2269

2270
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates= */ false);
340✔
2271
        if (r < 0)
340✔
UNCOV
2272
                return r;
×
2273

2274
        return 0;
2275
}
2276

2277
static char *destroy_tree(char *path) {
281✔
2278
        if (!path)
281✔
2279
                return NULL;
2280

2281
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
96✔
2282
                log_debug("Spawning process to nuke '%s'", path);
96✔
2283

2284
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
96✔
2285
        }
2286

2287
        return mfree(path);
96✔
2288
}
2289

2290
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
108,625✔
2291
        assert(rt);
108,625✔
2292

2293
        if (rt->manager)
108,625✔
2294
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
170✔
2295

2296
        rt->id = mfree(rt->id);
108,625✔
2297
        rt->tmp_dir = mfree(rt->tmp_dir);
108,625✔
2298
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
108,625✔
2299
        safe_close_pair(rt->userns_storage_socket);
108,625✔
2300
        safe_close_pair(rt->netns_storage_socket);
108,625✔
2301
        safe_close_pair(rt->ipcns_storage_socket);
108,625✔
2302
}
108,625✔
2303

2304
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
108,591✔
2305
        if (!rt)
108,591✔
2306
                return NULL;
2307

2308
        exec_shared_runtime_done(rt);
108,591✔
2309
        return mfree(rt);
108,591✔
2310
}
2311

2312
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
173✔
2313
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
111,470✔
2314

2315
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
57✔
2316
        if (!rt)
57✔
2317
                return NULL;
2318

2319
        assert(rt->n_ref > 0);
54✔
2320
        rt->n_ref--;
54✔
2321

2322
        if (rt->n_ref > 0)
54✔
2323
                return NULL;
2324

2325
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
54✔
2326
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
54✔
2327

2328
        return exec_shared_runtime_free(rt);
54✔
2329
}
2330

2331
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
108,591✔
2332
        _cleanup_free_ char *id_copy = NULL;
217,182✔
2333
        ExecSharedRuntime *n;
108,591✔
2334

2335
        assert(ret);
108,591✔
2336

2337
        id_copy = strdup(id);
108,591✔
2338
        if (!id_copy)
108,591✔
2339
                return -ENOMEM;
2340

2341
        n = new(ExecSharedRuntime, 1);
108,591✔
2342
        if (!n)
108,591✔
2343
                return -ENOMEM;
2344

2345
        *n = (ExecSharedRuntime) {
108,591✔
2346
                .id = TAKE_PTR(id_copy),
108,591✔
2347
                .userns_storage_socket = EBADF_PAIR,
2348
                .netns_storage_socket = EBADF_PAIR,
2349
                .ipcns_storage_socket = EBADF_PAIR,
2350
        };
2351

2352
        *ret = n;
108,591✔
2353
        return 0;
108,591✔
2354
}
2355

2356
static int exec_shared_runtime_add(
170✔
2357
                Manager *m,
2358
                const char *id,
2359
                char **tmp_dir,
2360
                char **var_tmp_dir,
2361
                int userns_storage_socket[2],
2362
                int netns_storage_socket[2],
2363
                int ipcns_storage_socket[2],
2364
                ExecSharedRuntime **ret) {
2365

2366
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
170✔
2367
        int r;
170✔
2368

2369
        assert(m);
170✔
2370
        assert(id);
170✔
2371

2372
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2373

2374
        r = exec_shared_runtime_allocate(&rt, id);
170✔
2375
        if (r < 0)
170✔
2376
                return r;
2377

2378
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
170✔
2379
        if (r < 0)
170✔
2380
                return r;
2381

2382
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
170✔
2383
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
170✔
2384
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
170✔
2385

2386
        if (userns_storage_socket) {
170✔
2387
                rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
170✔
2388
                rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
170✔
2389
        }
2390

2391
        if (netns_storage_socket) {
170✔
2392
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
170✔
2393
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
170✔
2394
        }
2395

2396
        if (ipcns_storage_socket) {
170✔
2397
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
170✔
2398
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
170✔
2399
        }
2400

2401
        rt->manager = m;
170✔
2402

2403
        if (ret)
170✔
2404
                *ret = rt;
78✔
2405
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2406
        TAKE_PTR(rt);
170✔
2407
        return 0;
170✔
2408
}
2409

2410
static int exec_shared_runtime_make(
6,419✔
2411
                Manager *m,
2412
                const ExecContext *c,
2413
                const char *id,
2414
                ExecSharedRuntime **ret) {
2415

2416
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
6,419✔
2417
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
19,257✔
2418
        int r;
6,419✔
2419

2420
        assert(m);
6,419✔
2421
        assert(c);
6,419✔
2422
        assert(id);
6,419✔
2423

2424
        /* It is not necessary to create ExecSharedRuntime object. */
2425
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
6,419✔
2426
                *ret = NULL;
6,341✔
2427
                return 0;
6,341✔
2428
        }
2429

2430
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
150✔
2431
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
72✔
UNCOV
2432
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
UNCOV
2433
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2434
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
72✔
2435
                if (r < 0)
72✔
2436
                        return r;
2437
        }
2438

2439
        if (c->user_namespace_path)
78✔
UNCOV
2440
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2441
                        return -errno;
×
2442

2443
        if (exec_needs_network_namespace(c))
78✔
2444
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
8✔
2445
                        return -errno;
×
2446

2447
        if (exec_needs_ipc_namespace(c))
78✔
2448
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2449
                        return -errno;
×
2450

2451
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
78✔
2452
        if (r < 0)
78✔
UNCOV
2453
                return r;
×
2454

2455
        return 1;
2456
}
2457

2458
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,511✔
2459
        ExecSharedRuntime *rt;
6,511✔
2460
        int r;
6,511✔
2461

2462
        assert(m);
6,511✔
2463
        assert(id);
6,511✔
2464
        assert(ret);
6,511✔
2465

2466
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,511✔
2467
        if (rt)
6,511✔
2468
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2469
                goto ref;
92✔
2470

2471
        if (!create) {
6,419✔
UNCOV
2472
                *ret = NULL;
×
UNCOV
2473
                return 0;
×
2474
        }
2475

2476
        /* If not found, then create a new object. */
2477
        r = exec_shared_runtime_make(m, c, id, &rt);
6,419✔
2478
        if (r < 0)
6,419✔
2479
                return r;
2480
        if (r == 0) {
6,419✔
2481
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2482
                *ret = NULL;
6,341✔
2483
                return 0;
6,341✔
2484
        }
2485

2486
ref:
78✔
2487
        /* increment reference counter. */
2488
        rt->n_ref++;
170✔
2489
        *ret = rt;
170✔
2490
        return 1;
170✔
2491
}
2492

2493
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
70✔
2494
        ExecSharedRuntime *rt;
70✔
2495

2496
        assert(m);
70✔
2497
        assert(f);
70✔
2498
        assert(fds);
70✔
2499

2500
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
186✔
2501
                fprintf(f, "exec-runtime=%s", rt->id);
116✔
2502

2503
                if (rt->tmp_dir)
116✔
2504
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
116✔
2505

2506
                if (rt->var_tmp_dir)
116✔
2507
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
116✔
2508

2509
                if (rt->userns_storage_socket[0] >= 0) {
116✔
2510
                        int copy;
×
2511

2512
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
UNCOV
2513
                        if (copy < 0)
×
UNCOV
2514
                                return copy;
×
2515

2516
                        fprintf(f, " userns-socket-0=%i", copy);
×
2517
                }
2518

2519
                if (rt->userns_storage_socket[1] >= 0) {
116✔
UNCOV
2520
                        int copy;
×
2521

2522
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
UNCOV
2523
                        if (copy < 0)
×
2524
                                return copy;
2525

UNCOV
2526
                        fprintf(f, " userns-socket-1=%i", copy);
×
2527
                }
2528

2529
                if (rt->netns_storage_socket[0] >= 0) {
116✔
2530
                        int copy;
4✔
2531

2532
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
4✔
2533
                        if (copy < 0)
4✔
2534
                                return copy;
2535

2536
                        fprintf(f, " netns-socket-0=%i", copy);
4✔
2537
                }
2538

2539
                if (rt->netns_storage_socket[1] >= 0) {
116✔
2540
                        int copy;
4✔
2541

2542
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
4✔
2543
                        if (copy < 0)
4✔
2544
                                return copy;
2545

2546
                        fprintf(f, " netns-socket-1=%i", copy);
4✔
2547
                }
2548

2549
                if (rt->ipcns_storage_socket[0] >= 0) {
116✔
UNCOV
2550
                        int copy;
×
2551

2552
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
UNCOV
2553
                        if (copy < 0)
×
2554
                                return copy;
2555

2556
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2557
                }
2558

2559
                if (rt->ipcns_storage_socket[1] >= 0) {
116✔
UNCOV
2560
                        int copy;
×
2561

2562
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
UNCOV
2563
                        if (copy < 0)
×
2564
                                return copy;
2565

UNCOV
2566
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2567
                }
2568

2569
                fputc('\n', f);
116✔
2570
        }
2571

2572
        return 0;
70✔
2573
}
2574

2575
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
111,300✔
2576
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
111,300✔
2577
        ExecSharedRuntime *rt = NULL;
111,300✔
2578
        int r;
111,300✔
2579

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

2585
        assert(u);
111,300✔
2586
        assert(key);
111,300✔
2587
        assert(value);
111,300✔
2588

2589
        /* Manager manages ExecSharedRuntime objects by the unit id.
2590
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2591
        if (isempty(u->id)) {
111,300✔
UNCOV
2592
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
UNCOV
2593
                return 0;
×
2594
        }
2595

2596
        if (u->manager) {
111,300✔
2597
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
111,300✔
UNCOV
2598
                        return log_oom();
×
2599

2600
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
111,300✔
2601
        }
2602
        if (!rt) {
111,300✔
2603
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
108,421✔
UNCOV
2604
                        return log_oom();
×
2605

2606
                rt = rt_create;
108,421✔
2607
        }
2608

2609
        if (streq(key, "tmp-dir")) {
111,300✔
2610
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2611
                        return -ENOMEM;
2612

2613
        } else if (streq(key, "var-tmp-dir")) {
111,300✔
UNCOV
2614
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2615
                        return -ENOMEM;
2616

2617
        } else if (streq(key, "netns-socket-0")) {
111,300✔
2618

UNCOV
2619
                safe_close(rt->netns_storage_socket[0]);
×
UNCOV
2620
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
UNCOV
2621
                if (rt->netns_storage_socket[0] < 0)
×
2622
                        return 0;
2623

2624
        } else if (streq(key, "netns-socket-1")) {
111,300✔
2625

UNCOV
2626
                safe_close(rt->netns_storage_socket[1]);
×
UNCOV
2627
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
UNCOV
2628
                if (rt->netns_storage_socket[1] < 0)
×
2629
                        return 0;
2630
        } else
2631
                return 0;
2632

2633
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2634
        if (rt_create && u->manager) {
×
UNCOV
2635
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
UNCOV
2636
                if (r < 0) {
×
2637
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
UNCOV
2638
                        return 0;
×
2639
                }
2640

UNCOV
2641
                rt_create->manager = u->manager;
×
2642

2643
                /* Avoid cleanup */
UNCOV
2644
                TAKE_PTR(rt_create);
×
2645
        }
2646

2647
        return 1;
2648
}
2649

2650
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
92✔
2651
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
92✔
2652
        char *id = NULL;
92✔
2653
        int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
92✔
2654
        const char *p, *v = ASSERT_PTR(value);
92✔
2655
        size_t n;
92✔
2656

2657
        assert(m);
92✔
2658
        assert(fds);
92✔
2659

2660
        n = strcspn(v, " ");
92✔
2661
        id = strndupa_safe(v, n);
92✔
2662
        if (v[n] != ' ')
92✔
UNCOV
2663
                goto finalize;
×
2664
        p = v + n + 1;
92✔
2665

2666
        v = startswith(p, "tmp-dir=");
92✔
2667
        if (v) {
92✔
2668
                n = strcspn(v, " ");
92✔
2669
                tmp_dir = strndup(v, n);
92✔
2670
                if (!tmp_dir)
92✔
UNCOV
2671
                        return log_oom();
×
2672
                if (v[n] != ' ')
92✔
UNCOV
2673
                        goto finalize;
×
2674
                p = v + n + 1;
92✔
2675
        }
2676

2677
        v = startswith(p, "var-tmp-dir=");
92✔
2678
        if (v) {
92✔
2679
                n = strcspn(v, " ");
92✔
2680
                var_tmp_dir = strndup(v, n);
92✔
2681
                if (!var_tmp_dir)
92✔
UNCOV
2682
                        return log_oom();
×
2683
                if (v[n] != ' ')
92✔
2684
                        goto finalize;
90✔
2685
                p = v + n + 1;
2✔
2686
        }
2687

2688
        v = startswith(p, "userns-socket-0=");
2✔
2689
        if (v) {
2✔
UNCOV
2690
                char *buf;
×
2691

2692
                n = strcspn(v, " ");
×
UNCOV
2693
                buf = strndupa_safe(v, n);
×
2694

2695
                userns_fdpair[0] = deserialize_fd(fds, buf);
×
2696
                if (userns_fdpair[0] < 0)
×
2697
                        return userns_fdpair[0];
UNCOV
2698
                if (v[n] != ' ')
×
UNCOV
2699
                        goto finalize;
×
UNCOV
2700
                p = v + n + 1;
×
2701
        }
2702

2703
        v = startswith(p, "userns-socket-1=");
2✔
2704
        if (v) {
2✔
UNCOV
2705
                char *buf;
×
2706

2707
                n = strcspn(v, " ");
×
UNCOV
2708
                buf = strndupa_safe(v, n);
×
2709

2710
                userns_fdpair[1] = deserialize_fd(fds, buf);
×
2711
                if (userns_fdpair[1] < 0)
×
2712
                        return userns_fdpair[1];
UNCOV
2713
                if (v[n] != ' ')
×
UNCOV
2714
                        goto finalize;
×
UNCOV
2715
                p = v + n + 1;
×
2716
        }
2717

2718
        v = startswith(p, "netns-socket-0=");
2✔
2719
        if (v) {
2✔
2720
                char *buf;
2✔
2721

2722
                n = strcspn(v, " ");
2✔
2723
                buf = strndupa_safe(v, n);
2✔
2724

2725
                netns_fdpair[0] = deserialize_fd(fds, buf);
2✔
2726
                if (netns_fdpair[0] < 0)
2✔
2727
                        return netns_fdpair[0];
2728
                if (v[n] != ' ')
2✔
UNCOV
2729
                        goto finalize;
×
2730
                p = v + n + 1;
2✔
2731
        }
2732

2733
        v = startswith(p, "netns-socket-1=");
2✔
2734
        if (v) {
2✔
2735
                char *buf;
2✔
2736

2737
                n = strcspn(v, " ");
2✔
2738
                buf = strndupa_safe(v, n);
2✔
2739

2740
                netns_fdpair[1] = deserialize_fd(fds, buf);
2✔
2741
                if (netns_fdpair[1] < 0)
2✔
2742
                        return netns_fdpair[1];
2743
                if (v[n] != ' ')
2✔
2744
                        goto finalize;
2✔
2745
                p = v + n + 1;
×
2746
        }
2747

2748
        v = startswith(p, "ipcns-socket-0=");
×
2749
        if (v) {
×
UNCOV
2750
                char *buf;
×
2751

2752
                n = strcspn(v, " ");
×
UNCOV
2753
                buf = strndupa_safe(v, n);
×
2754

2755
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2756
                if (ipcns_fdpair[0] < 0)
×
2757
                        return ipcns_fdpair[0];
UNCOV
2758
                if (v[n] != ' ')
×
2759
                        goto finalize;
×
2760
                p = v + n + 1;
×
2761
        }
2762

2763
        v = startswith(p, "ipcns-socket-1=");
×
2764
        if (v) {
×
UNCOV
2765
                char *buf;
×
2766

2767
                n = strcspn(v, " ");
×
UNCOV
2768
                buf = strndupa_safe(v, n);
×
2769

UNCOV
2770
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2771
                if (ipcns_fdpair[1] < 0)
×
2772
                        return ipcns_fdpair[1];
2773
        }
2774

UNCOV
2775
finalize:
×
2776
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
92✔
2777
        if (r < 0)
92✔
UNCOV
2778
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2779
        return 0;
2780
}
2781

2782
void exec_shared_runtime_vacuum(Manager *m) {
1,536✔
2783
        ExecSharedRuntime *rt;
1,536✔
2784

2785
        assert(m);
1,536✔
2786

2787
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2788

2789
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,628✔
2790
                if (rt->n_ref > 0)
92✔
2791
                        continue;
92✔
2792

UNCOV
2793
                (void) exec_shared_runtime_free(rt);
×
2794
        }
2795
}
1,536✔
2796

2797
int exec_runtime_make(
6,511✔
2798
                const Unit *unit,
2799
                const ExecContext *context,
2800
                ExecSharedRuntime *shared,
2801
                DynamicCreds *creds,
2802
                ExecRuntime **ret) {
2803
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
6,511✔
2804
        _cleanup_free_ char *ephemeral = NULL;
6,511✔
2805
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
6,511✔
2806
        int r;
6,511✔
2807

2808
        assert(unit);
6,511✔
2809
        assert(context);
6,511✔
2810
        assert(ret);
6,511✔
2811

2812
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,511✔
2813
                *ret = NULL;
6,338✔
2814
                return 0;
6,338✔
2815
        }
2816

2817
        if (exec_needs_ephemeral(context)) {
173✔
2818
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2819
                if (r < 0)
×
2820
                        return r;
2821

2822
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2823
                if (r < 0)
×
2824
                        return r;
2825

UNCOV
2826
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
UNCOV
2827
                        return -errno;
×
2828
        }
2829

2830
        rt = new(ExecRuntime, 1);
173✔
2831
        if (!rt)
173✔
2832
                return -ENOMEM;
2833

2834
        *rt = (ExecRuntime) {
173✔
2835
                .shared = shared,
2836
                .dynamic_creds = creds,
2837
                .ephemeral_copy = TAKE_PTR(ephemeral),
173✔
2838
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
173✔
2839
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
173✔
2840
        };
2841

2842
        *ret = TAKE_PTR(rt);
173✔
2843
        return 1;
173✔
2844
}
2845

2846
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
52,628✔
2847
        if (!rt)
52,628✔
2848
                return NULL;
2849

2850
        exec_shared_runtime_unref(rt->shared);
173✔
2851
        dynamic_creds_unref(rt->dynamic_creds);
173✔
2852

2853
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
173✔
2854

2855
        safe_close_pair(rt->ephemeral_storage_socket);
173✔
2856
        return mfree(rt);
173✔
2857
}
2858

2859
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
6,310✔
2860
        if (!rt)
6,310✔
2861
                return NULL;
2862

2863
        rt->shared = exec_shared_runtime_destroy(rt->shared);
57✔
2864
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
57✔
2865
        return exec_runtime_free(rt);
57✔
2866
}
2867

2868
void exec_runtime_clear(ExecRuntime *rt) {
34✔
2869
        if (!rt)
34✔
2870
                return;
2871

2872
        safe_close_pair(rt->ephemeral_storage_socket);
34✔
2873
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
34✔
2874
}
2875

2876
void exec_params_shallow_clear(ExecParameters *p) {
2,597✔
2877
        if (!p)
2,597✔
2878
                return;
2879

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

2883
        p->environment = strv_free(p->environment);
2,597✔
2884
        p->fd_names = strv_free(p->fd_names);
2,597✔
2885
        p->files_env = strv_free(p->files_env);
2,597✔
2886
        p->fds = mfree(p->fds);
2,597✔
2887
        p->root_directory_fd = safe_close(p->root_directory_fd);
2,597✔
2888
        p->exec_fd = safe_close(p->exec_fd);
2,597✔
2889
        p->user_lookup_fd = -EBADF;
2,597✔
2890
        p->bpf_restrict_fs_map_fd = -EBADF;
2,597✔
2891
        p->unit_id = mfree(p->unit_id);
2,597✔
2892
        p->invocation_id = SD_ID128_NULL;
2,597✔
2893
        p->invocation_id_string[0] = '\0';
2,597✔
2894
        p->confirm_spawn = mfree(p->confirm_spawn);
2,597✔
2895
}
2896

2897
void exec_params_deep_clear(ExecParameters *p) {
34✔
2898
        if (!p)
34✔
2899
                return;
2900

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

2905
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
34✔
2906

2907
        p->cgroup_path = mfree(p->cgroup_path);
34✔
2908

2909
        if (p->prefix) {
34✔
2910
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
34✔
2911
                p->prefix = mfree(p->prefix);
34✔
2912
        }
2913

2914
        p->received_credentials_directory = mfree(p->received_credentials_directory);
34✔
2915
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
34✔
2916

2917
        if (p->idle_pipe) {
34✔
UNCOV
2918
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2919
                p->idle_pipe = NULL;
×
2920
        }
2921

2922
        p->stdin_fd = safe_close(p->stdin_fd);
34✔
2923
        p->stdout_fd = safe_close(p->stdout_fd);
34✔
2924
        p->stderr_fd = safe_close(p->stderr_fd);
34✔
2925
        p->root_directory_fd = safe_close(p->root_directory_fd);
34✔
2926

2927
        p->notify_socket = mfree(p->notify_socket);
34✔
2928

2929
        open_file_free_many(&p->open_files);
34✔
2930

2931
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
34✔
2932

2933
        exec_params_shallow_clear(p);
34✔
2934
}
2935

2936
void exec_directory_done(ExecDirectory *d) {
263,025✔
2937
        if (!d)
263,025✔
2938
                return;
2939

2940
        FOREACH_ARRAY(i, d->items, d->n_items) {
264,667✔
2941
                free(i->path);
1,642✔
2942
                strv_free(i->symlinks);
1,642✔
2943
        }
2944

2945
        d->items = mfree(d->items);
263,025✔
2946
        d->n_items = 0;
263,025✔
2947
        d->mode = 0755;
263,025✔
2948
}
2949

2950
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
4,627✔
2951
        assert(d);
4,627✔
2952
        assert(path);
4,627✔
2953

2954
        FOREACH_ARRAY(i, d->items, d->n_items)
7,071✔
2955
                if (path_equal(i->path, path))
2,459✔
2956
                        return i;
2957

2958
        return NULL;
2959
}
2960

2961
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
4,627✔
UNCOV
2962
        _cleanup_strv_free_ char **s = NULL;
×
2963
        _cleanup_free_ char *p = NULL;
4,627✔
2964
        ExecDirectoryItem *existing;
4,627✔
2965
        int r;
4,627✔
2966

2967
        assert(d);
4,627✔
2968
        assert(path);
4,627✔
2969

2970
        existing = exec_directory_find(d, path);
4,627✔
2971
        if (existing) {
4,627✔
2972
                r = strv_extend(&existing->symlinks, symlink);
15✔
2973
                if (r < 0)
15✔
2974
                        return r;
2975

2976
                existing->flags |= flags;
15✔
2977

2978
                return 0; /* existing item is updated */
15✔
2979
        }
2980

2981
        p = strdup(path);
4,612✔
2982
        if (!p)
4,612✔
2983
                return -ENOMEM;
2984

2985
        if (symlink) {
4,612✔
2986
                s = strv_new(symlink);
6✔
2987
                if (!s)
6✔
2988
                        return -ENOMEM;
2989
        }
2990

2991
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
4,612✔
2992
                return -ENOMEM;
2993

2994
        d->items[d->n_items++] = (ExecDirectoryItem) {
4,612✔
2995
                .path = TAKE_PTR(p),
4,612✔
2996
                .symlinks = TAKE_PTR(s),
4,612✔
2997
                .flags = flags,
2998
        };
2999

3000
        return 1; /* new item is added */
4,612✔
3001
}
3002

3003
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
932✔
3004
        assert(a);
932✔
3005
        assert(b);
932✔
3006

3007
        return path_compare(a->path, b->path);
932✔
3008
}
3009

3010
void exec_directory_sort(ExecDirectory *d) {
144,741✔
3011
        assert(d);
144,741✔
3012

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

3018
        if (d->n_items <= 1)
144,741✔
3019
                return;
3020

3021
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
166✔
3022

3023
        for (size_t i = 1; i < d->n_items; i++)
725✔
3024
                for (size_t j = 0; j < i; j++)
1,864✔
3025
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,320✔
3026
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3027
                                break;
15✔
3028
                        }
3029
}
3030

3031
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3032
        ExecDirectoryType t;
4✔
3033

3034
        assert(s);
4✔
3035

3036
        if (streq(s, "all"))
4✔
3037
                return EXEC_CLEAN_ALL;
3038
        if (streq(s, "fdstore"))
3✔
3039
                return EXEC_CLEAN_FDSTORE;
3040

3041
        t = exec_resource_type_from_string(s);
2✔
3042
        if (t < 0)
2✔
3043
                return (ExecCleanMask) t;
3044

3045
        return 1U << t;
2✔
3046
}
3047

3048
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
3049
        [EXEC_INPUT_NULL]      = "null",
3050
        [EXEC_INPUT_TTY]       = "tty",
3051
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
3052
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
3053
        [EXEC_INPUT_SOCKET]    = "socket",
3054
        [EXEC_INPUT_NAMED_FD]  = "fd",
3055
        [EXEC_INPUT_DATA]      = "data",
3056
        [EXEC_INPUT_FILE]      = "file",
3057
};
3058

3059
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,664✔
3060

3061
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
3062
        [EXEC_OUTPUT_INHERIT]             = "inherit",
3063
        [EXEC_OUTPUT_NULL]                = "null",
3064
        [EXEC_OUTPUT_TTY]                 = "tty",
3065
        [EXEC_OUTPUT_KMSG]                = "kmsg",
3066
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
3067
        [EXEC_OUTPUT_JOURNAL]             = "journal",
3068
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
3069
        [EXEC_OUTPUT_SOCKET]              = "socket",
3070
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
3071
        [EXEC_OUTPUT_FILE]                = "file",
3072
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
3073
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
3074
};
3075

3076
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,540✔
3077

3078
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3079
        [EXEC_UTMP_INIT]  = "init",
3080
        [EXEC_UTMP_LOGIN] = "login",
3081
        [EXEC_UTMP_USER]  = "user",
3082
};
3083

3084
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,710✔
3085

3086
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3087
        [EXEC_PRESERVE_NO]      = "no",
3088
        [EXEC_PRESERVE_YES]     = "yes",
3089
        [EXEC_PRESERVE_RESTART] = "restart",
3090
};
3091

3092
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
19,492✔
3093

3094
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3095
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3096
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3097
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3098
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3099
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3100
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3101
};
3102

3103
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3104

3105
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3106
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3107
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3108
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3109
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3110
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3111
};
3112

UNCOV
3113
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3114

3115
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
3116
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
3117
 * directories, specifically .timer units with their timestamp touch file. */
3118
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3119
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
3120
        [EXEC_DIRECTORY_STATE]         = "state",
3121
        [EXEC_DIRECTORY_CACHE]         = "cache",
3122
        [EXEC_DIRECTORY_LOGS]          = "logs",
3123
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3124
};
3125

3126
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
269✔
3127

3128
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3129
        [EXEC_KEYRING_INHERIT] = "inherit",
3130
        [EXEC_KEYRING_PRIVATE] = "private",
3131
        [EXEC_KEYRING_SHARED]  = "shared",
3132
};
3133

3134
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
16,233✔
3135

3136
static const char* const memory_thp_table[_MEMORY_THP_MAX] = {
3137
        [MEMORY_THP_INHERIT] = "inherit",
3138
        [MEMORY_THP_DISABLE] = "disable",
3139
        [MEMORY_THP_MADVISE] = "madvise",
3140
        [MEMORY_THP_SYSTEM]  = "system",
3141
};
3142

3143
DEFINE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP);
16,046✔
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