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

systemd / systemd / 22246189288

20 Feb 2026 07:59PM UTC coverage: 72.238% (-0.2%) from 72.47%
22246189288

push

github

bluca
Add BNCF NewBook 11 ACCEL_MOUNT_MATRIX  to 60-sensor.hwdb

Corrects DE autorotation

Device description: https://www.bncfai.com/product/773/

313540 of 434040 relevant lines covered (72.24%)

1346131.39 hits per line

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

79.91
/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,892✔
68
        assert(context);
12,892✔
69

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

73
        if (context->tty_path)
12,044✔
74
                return context->tty_path;
557✔
75

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

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

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

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

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

95
        if (!tty_path)
524✔
96
                tty_path = exec_context_tty_path(context);
269✔
97

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

102
        /* Fill in data from kernel command line if anything is unspecified */
103
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
524✔
104
                (void) proc_cmdline_tty_size(
474✔
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 &&
669✔
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);
524✔
122
}
123

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

128
        assert(context);
11,866✔
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,866✔
136

137
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
11,866✔
138
                fd = parameters->stdout_fd;
25✔
139
        else if (path && exec_context_has_tty(context)) {
11,841✔
140
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
289✔
141
                if (fd < 0)
289✔
142
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
34✔
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();
280✔
150
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
280✔
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))
280✔
153
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
154
        else if (lock_fd < 0)
280✔
155
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
156

157
        if (context->tty_reset)
280✔
158
                (void) terminal_reset_defensive(
217✔
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));
214✔
162

163
        r = exec_context_apply_tty_size(context, fd, fd, path);
280✔
164
        if (r < 0)
280✔
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)) {
525✔
168
                sd_id128_t context_id;
21✔
169

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

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

184
        if (context->tty_vhangup)
280✔
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);
280✔
190

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

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

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

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

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

208
        return context->private_ipc || context->ipc_namespace_path;
56,305✔
209
}
210

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

215
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
82,634✔
216
        assert(context);
82,634✔
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)) {
82,634✔
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;
82,634✔
229
}
230

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

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

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

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

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

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

249
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
90,972✔
250
        assert(context);
90,972✔
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,972✔
254
                return false;
255

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

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

264
        assert(context);
33,156✔
265

266
        if (context->root_image ||
33,156✔
267
            context->root_mstack)
33,071✔
268
                return true;
269

270
        if (context->root_directory_as_fd)
33,051✔
271
                return true;
272

273
        if (!strv_isempty(context->read_write_paths) ||
33,045✔
274
            !strv_isempty(context->read_only_paths) ||
30,736✔
275
            !strv_isempty(context->inaccessible_paths) ||
30,729✔
276
            !strv_isempty(context->exec_paths) ||
30,704✔
277
            !strv_isempty(context->no_exec_paths))
30,704✔
278
                return true;
279

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

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

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

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

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

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

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

301
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
29,784✔
302
                return true;
303

304
        if (context->private_devices ||
29,266✔
305
            context->private_mounts > 0 ||
28,897✔
306
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,421✔
307
            context->protect_system != PROTECT_SYSTEM_NO ||
28,392✔
308
            context->protect_home != PROTECT_HOME_NO ||
28,392✔
309
            context->protect_kernel_tunables ||
28,392✔
310
            context->protect_kernel_modules ||
28,392✔
311
            context->protect_kernel_logs ||
53,387✔
312
            exec_needs_cgroup_mount(context) ||
26,692✔
313
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,674✔
314
            context->proc_subset != PROC_SUBSET_ALL ||
26,600✔
315
            context->private_bpf != PRIVATE_BPF_NO ||
53,182✔
316
            exec_needs_ipc_namespace(context) ||
53,182✔
317
            exec_needs_pid_namespace(context, params))
26,591✔
318
                return true;
2,716✔
319

320
        if (context->root_directory) {
26,550✔
321
                if (exec_context_get_effective_mount_apivfs(context))
4✔
322
                        return true;
323

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

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

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

339
        if (exec_context_get_effective_bind_log_sockets(context))
26,546✔
340
                return true;
341

342
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
158,908✔
343
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
137,413✔
344
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,045✔
345
                                return true;
346

347
        return false;
348
}
349

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

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

357
        if (!needs_sandboxing)
3,218✔
358
                return NULL;
359

360
        if (!exec_context_with_rootfs(context))
3,218✔
361
                return NULL;
362

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

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

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

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

375
        if (params->debug_invocation)
20,362✔
376
                return LOG_DEBUG;
377

378
        return exec_log_level_max(context);
20,358✔
379
}
380

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

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

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

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

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

398
        return true;
399
}
400

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

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

412
        const char *subgroup = NULL;
10,895✔
413
        char *p;
10,895✔
414

415
        assert(params);
10,895✔
416
        assert(c);
10,895✔
417
        assert(ret);
10,895✔
418

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

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

436
        if (subgroup)
664✔
437
                p = path_join(prefix, subgroup);
710✔
438
        else
439
                p = strdup(strempty(prefix));
10,193✔
440
        if (!p)
10,895✔
441
                return -ENOMEM;
442

443
        *ret = p;
10,895✔
444
        return !!subgroup;
10,895✔
445
}
446

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

450
        return c->cpu_affinity_from_numa;
2,959✔
451
}
452

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

458
        if (!DEBUG_LOGGING)
2,481✔
459
                return;
2,481✔
460

461
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,962✔
462

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

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

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

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

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

497
        LOG_CONTEXT_PUSH_UNIT(unit);
4,962✔
498

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

623
void exec_context_init(ExecContext *c) {
65,129✔
624
        assert(c);
65,129✔
625

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

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

656
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
390,774✔
657
                d->mode = 0755;
325,645✔
658

659
        numa_policy_reset(&c->numa_policy);
65,129✔
660

661
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
65,129✔
662
}
65,129✔
663

664
void exec_context_done(ExecContext *c) {
55,017✔
665
        assert(c);
55,017✔
666

667
        c->environment = strv_free(c->environment);
55,017✔
668
        c->environment_files = strv_free(c->environment_files);
55,017✔
669
        c->pass_environment = strv_free(c->pass_environment);
55,017✔
670
        c->unset_environment = strv_free(c->unset_environment);
55,017✔
671

672
        rlimit_free_all(c->rlimit);
55,017✔
673

674
        for (size_t l = 0; l < 3; l++) {
220,068✔
675
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
165,051✔
676
                c->stdio_file[l] = mfree(c->stdio_file[l]);
165,051✔
677
        }
678

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

694
        c->supplementary_groups = strv_free(c->supplementary_groups);
55,017✔
695

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

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

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

719
        cpu_set_done(&c->cpu_set);
55,017✔
720
        numa_policy_reset(&c->numa_policy);
55,017✔
721

722
        c->utmp_id = mfree(c->utmp_id);
55,017✔
723
        c->selinux_context = mfree(c->selinux_context);
55,017✔
724
        c->apparmor_profile = mfree(c->apparmor_profile);
55,017✔
725
        c->smack_process_label = mfree(c->smack_process_label);
55,017✔
726

727
        c->restrict_filesystems = set_free(c->restrict_filesystems);
55,017✔
728

729
        c->syscall_filter = hashmap_free(c->syscall_filter);
55,017✔
730
        c->syscall_archs = set_free(c->syscall_archs);
55,017✔
731
        c->syscall_log = hashmap_free(c->syscall_log);
55,017✔
732
        c->address_families = set_free(c->address_families);
55,017✔
733

734
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
330,102✔
735
                exec_directory_done(d);
275,085✔
736

737
        c->log_level_max = -1;
55,017✔
738

739
        exec_context_free_log_extra_fields(c);
55,017✔
740
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
55,017✔
741
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
55,017✔
742

743
        c->log_ratelimit = (RateLimit) {};
55,017✔
744

745
        c->stdin_data = mfree(c->stdin_data);
55,017✔
746
        c->stdin_data_size = 0;
55,017✔
747

748
        c->user_namespace_path = mfree(c->user_namespace_path);
55,017✔
749
        c->network_namespace_path = mfree(c->network_namespace_path);
55,017✔
750
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
55,017✔
751

752
        c->log_namespace = mfree(c->log_namespace);
55,017✔
753

754
        c->load_credentials = hashmap_free(c->load_credentials);
55,017✔
755
        c->set_credentials = hashmap_free(c->set_credentials);
55,017✔
756
        c->import_credentials = ordered_set_free(c->import_credentials);
55,017✔
757

758
        c->root_image_policy = image_policy_free(c->root_image_policy);
55,017✔
759
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
55,017✔
760
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
55,017✔
761

762
        c->private_hostname = mfree(c->private_hostname);
55,017✔
763
}
55,017✔
764

765
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
6,289✔
766
        assert(c);
6,289✔
767

768
        if (!runtime_prefix)
6,289✔
769
                return 0;
770

771
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
6,304✔
772
                _cleanup_free_ char *p = NULL;
15✔
773

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

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

785
                STRV_FOREACH(symlink, i->symlinks) {
15✔
786
                        _cleanup_free_ char *symlink_abs = NULL;
×
787

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

795
                        (void) unlink(symlink_abs);
×
796
                }
797
        }
798

799
        return 0;
800
}
801

802
int exec_context_destroy_mount_ns_dir(Unit *u) {
12,890✔
803
        _cleanup_free_ char *p = NULL;
12,890✔
804

805
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
12,890✔
806
                return 0;
807

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

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

816
        return 0;
817
}
818

819
void exec_command_done(ExecCommand *c) {
116,021✔
820
        assert(c);
116,021✔
821

822
        c->path = mfree(c->path);
116,021✔
823
        c->argv = strv_free(c->argv);
116,021✔
824
}
116,021✔
825

826
void exec_command_done_array(ExecCommand *c, size_t n) {
32,619✔
827
        FOREACH_ARRAY(i, c, n)
130,474✔
828
                exec_command_done(i);
97,855✔
829
}
32,619✔
830

831
ExecCommand* exec_command_free(ExecCommand *c) {
18,098✔
832
        if (!c)
18,098✔
833
                return NULL;
834

835
        exec_command_done(c);
18,098✔
836
        return mfree(c);
18,098✔
837
}
838

839
ExecCommand* exec_command_free_list(ExecCommand *c) {
157,223✔
840
        ExecCommand *i;
157,223✔
841

842
        while ((i = LIST_POP(command, c)))
175,321✔
843
                exec_command_free(i);
18,098✔
844

845
        return NULL;
157,223✔
846
}
847

848
void exec_command_free_array(ExecCommand **c, size_t n) {
22,330✔
849
        FOREACH_ARRAY(i, c, n)
179,532✔
850
                *i = exec_command_free_list(*i);
157,202✔
851
}
22,330✔
852

853
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,718✔
854
        FOREACH_ARRAY(i, c, n)
30,871✔
855
                exec_status_reset(&i->exec_status);
23,153✔
856
}
7,718✔
857

858
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,442✔
859
        FOREACH_ARRAY(i, c, n)
39,057✔
860
                LIST_FOREACH(command, z, *i)
36,020✔
861
                        exec_status_reset(&z->exec_status);
2,405✔
862
}
5,442✔
863

864
typedef struct InvalidEnvInfo {
865
        const Unit *unit;
866
        const char *path;
867
} InvalidEnvInfo;
868

869
static void invalid_env(const char *p, void *userdata) {
×
870
        InvalidEnvInfo *info = userdata;
×
871

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

875
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,420✔
876
        assert(c);
39,420✔
877

878
        switch (fd_index) {
39,420✔
879

880
        case STDIN_FILENO:
13,140✔
881
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,140✔
882
                        return NULL;
883

884
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
885

886
        case STDOUT_FILENO:
13,140✔
887
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,140✔
888
                        return NULL;
889

890
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
891

892
        case STDERR_FILENO:
13,140✔
893
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,140✔
894
                        return NULL;
895

896
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
897

898
        default:
899
                return NULL;
900
        }
901
}
902

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

907
        assert(c);
2,481✔
908
        assert(ret);
2,481✔
909

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

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

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

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

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

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

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

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

954
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
955
                        }
956

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

964
                                strv_free_and_replace(v, m);
×
965
                        }
966
                }
967
        }
968

969
        *ret = TAKE_PTR(v);
2,481✔
970

971
        return 0;
2,481✔
972
}
973

974
static bool tty_may_match_dev_console(const char *tty) {
245✔
975
        _cleanup_free_ char *resolved = NULL;
245✔
976

977
        if (!tty)
245✔
978
                return true;
979

980
        tty = skip_dev_prefix(tty);
245✔
981

982
        /* trivial identity? */
983
        if (streq(tty, "console"))
245✔
984
                return true;
985

986
        if (resolve_dev_console(&resolved) < 0)
39✔
987
                return true; /* if we could not resolve, assume it may */
988

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

993
static bool exec_context_may_touch_tty(const ExecContext *ec) {
26,916✔
994
        assert(ec);
26,916✔
995

996
        return ec->tty_reset ||
53,601✔
997
                ec->tty_vhangup ||
26,685✔
998
                ec->tty_vt_disallocate ||
26,685✔
999
                exec_input_is_terminal(ec->std_input) ||
26,685✔
1000
                ec->std_output == EXEC_OUTPUT_TTY ||
53,601✔
1001
                ec->std_error == EXEC_OUTPUT_TTY;
26,636✔
1002
}
1003

1004
bool exec_context_may_touch_console(const ExecContext *ec) {
25,230✔
1005

1006
        return exec_context_may_touch_tty(ec) &&
25,475✔
1007
               tty_may_match_dev_console(exec_context_tty_path(ec));
245✔
1008
}
1009

1010
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,188✔
1011
        assert(c);
1,188✔
1012

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

1020
        if (!c->tty_reset)
1,188✔
1021
                return false;
1022

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

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

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

1036
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
3,104✔
1037
        assert(f);
3,104✔
1038
        assert(prefix);
3,104✔
1039
        assert(name);
3,104✔
1040

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

1048
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1049
        assert(p);
×
1050
        assert(f);
×
1051

1052
        prefix = strempty(prefix);
×
1053

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

1080
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1081
        strv_dump(f, prefix, "Environment", p->environment);
×
1082
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1083

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

1087
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1088
}
×
1089

1090
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
388✔
1091
        int r;
388✔
1092

1093
        assert(c);
388✔
1094
        assert(f);
388✔
1095

1096
        prefix = strempty(prefix);
388✔
1097

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

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

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

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

1174
        if (c->root_image)
388✔
1175
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1176

1177
        if (c->root_image_options) {
388✔
1178
                _cleanup_free_ char *opts_str = NULL;
×
1179

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

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

1191
        if (c->root_hash_path)
388✔
1192
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1193

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

1202
        if (c->root_hash_sig_path)
388✔
1203
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1204

1205
        if (c->root_verity)
388✔
1206
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1207

1208
        if (c->root_mstack)
388✔
1209
                fprintf(f, "%sRootMStack: %s\n", prefix, c->root_mstack);
×
1210

1211
        STRV_FOREACH(e, c->environment)
395✔
1212
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
7✔
1213

1214
        STRV_FOREACH(e, c->environment_files)
388✔
1215
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1216

1217
        STRV_FOREACH(e, c->pass_environment)
400✔
1218
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
12✔
1219

1220
        STRV_FOREACH(e, c->unset_environment)
403✔
1221
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
15✔
1222

1223
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
388✔
1224

1225
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
2,328✔
1226
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
1,940✔
1227

1228
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,963✔
1229
                        fprintf(f,
23✔
1230
                                "%s%s: %s%s\n",
1231
                                prefix,
1232
                                exec_directory_type_to_string(dt),
1233
                                c->directories[dt].items[i].path,
1234
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
23✔
1235

1236
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
23✔
1237
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1238
                }
1239
        }
1240

1241
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
388✔
1242

1243
        if (c->memory_ksm >= 0)
388✔
1244
                fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
2✔
1245

1246
        if (c->nice_set)
388✔
1247
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
2✔
1248

1249
        if (c->oom_score_adjust_set)
388✔
1250
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
16✔
1251

1252
        if (c->coredump_filter_set)
388✔
1253
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1254

1255
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
6,596✔
1256
                if (c->rlimit[i]) {
6,208✔
1257
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
352✔
1258
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1259
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
352✔
1260
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
352✔
1261
                }
1262

1263
        if (c->ioprio_is_set) {
388✔
1264
                _cleanup_free_ char *class_str = NULL;
3✔
1265

1266
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
3✔
1267
                if (r >= 0)
3✔
1268
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
3✔
1269

1270
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
3✔
1271
        }
1272

1273
        if (c->cpu_sched_set) {
388✔
1274
                _cleanup_free_ char *policy_str = NULL;
×
1275

1276
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1277
                if (r >= 0)
×
1278
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1279

1280
                fprintf(f,
×
1281
                        "%sCPUSchedulingPriority: %i\n"
1282
                        "%sCPUSchedulingResetOnFork: %s\n",
1283
                        prefix, c->cpu_sched_priority,
×
1284
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1285
        }
1286

1287
        if (c->cpu_set.set) {
388✔
1288
                _cleanup_free_ char *affinity = NULL;
×
1289

1290
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1291
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1292
        }
1293

1294
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
388✔
1295
                _cleanup_free_ char *nodes = NULL;
1✔
1296

1297
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1298
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1299
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1300
        }
1301

1302
        if (c->timer_slack_nsec != NSEC_INFINITY)
388✔
1303
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1304

1305
        fprintf(f,
388✔
1306
                "%sStandardInput: %s\n"
1307
                "%sStandardOutput: %s\n"
1308
                "%sStandardError: %s\n",
1309
                prefix, exec_input_to_string(c->std_input),
388✔
1310
                prefix, exec_output_to_string(c->std_output),
388✔
1311
                prefix, exec_output_to_string(c->std_error));
388✔
1312

1313
        if (c->std_input == EXEC_INPUT_NAMED_FD)
388✔
1314
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1315
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
388✔
1316
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1317
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
388✔
1318
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1319

1320
        if (c->std_input == EXEC_INPUT_FILE)
388✔
1321
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1322
        if (c->std_output == EXEC_OUTPUT_FILE)
388✔
1323
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1324
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
388✔
1325
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1326
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
388✔
1327
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1328
        if (c->std_error == EXEC_OUTPUT_FILE)
388✔
1329
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1330
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
388✔
1331
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1332
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
388✔
1333
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1334

1335
        if (c->tty_path)
388✔
1336
                fprintf(f,
2✔
1337
                        "%sTTYPath: %s\n"
1338
                        "%sTTYReset: %s\n"
1339
                        "%sTTYVHangup: %s\n"
1340
                        "%sTTYVTDisallocate: %s\n"
1341
                        "%sTTYRows: %u\n"
1342
                        "%sTTYColumns: %u\n",
1343
                        prefix, c->tty_path,
1344
                        prefix, yes_no(c->tty_reset),
2✔
1345
                        prefix, yes_no(c->tty_vhangup),
2✔
1346
                        prefix, yes_no(c->tty_vt_disallocate),
2✔
1347
                        prefix, c->tty_rows,
2✔
1348
                        prefix, c->tty_cols);
2✔
1349

1350
        if (IN_SET(c->std_output,
388✔
1351
                   EXEC_OUTPUT_KMSG,
1352
                   EXEC_OUTPUT_JOURNAL,
1353
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1354
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1355
            IN_SET(c->std_error,
24✔
1356
                   EXEC_OUTPUT_KMSG,
1357
                   EXEC_OUTPUT_JOURNAL,
1358
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1359
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1360

1361
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
364✔
1362

1363
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
364✔
1364
                if (r >= 0)
364✔
1365
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
364✔
1366

1367
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
364✔
1368
                if (r >= 0)
364✔
1369
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
364✔
1370
        }
1371

1372
        if (c->log_level_max >= 0) {
388✔
1373
                _cleanup_free_ char *t = NULL;
1✔
1374

1375
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1376

1377
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1378
        }
1379

1380
        if (c->log_ratelimit.interval > 0)
388✔
1381
                fprintf(f,
×
1382
                        "%sLogRateLimitIntervalSec: %s\n",
1383
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1384

1385
        if (c->log_ratelimit.burst > 0)
388✔
1386
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1387

1388
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
388✔
1389
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1390

1391
                char *pattern;
×
1392
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1393
                        fprintf(f, " %s", pattern);
×
1394
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1395
                        fprintf(f, " ~%s", pattern);
×
1396
                fputc('\n', f);
×
1397
        }
1398

1399
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
392✔
1400
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1401
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1402
                fputc('\n', f);
4✔
1403
        }
1404

1405
        if (c->log_namespace)
388✔
1406
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1407

1408
        if (c->secure_bits) {
388✔
1409
                _cleanup_free_ char *str = NULL;
×
1410

1411
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1412
                if (r >= 0)
×
1413
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1414
        }
1415

1416
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
388✔
1417
                _cleanup_free_ char *str = NULL;
388✔
1418

1419
                r = capability_set_to_string(c->capability_bounding_set, &str);
388✔
1420
                if (r >= 0)
388✔
1421
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
388✔
1422
        }
1423

1424
        if (c->capability_ambient_set != 0) {
388✔
1425
                _cleanup_free_ char *str = NULL;
3✔
1426

1427
                r = capability_set_to_string(c->capability_ambient_set, &str);
3✔
1428
                if (r >= 0)
3✔
1429
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
3✔
1430
        }
1431

1432
        if (c->user)
388✔
1433
                fprintf(f, "%sUser: %s\n", prefix, c->user);
7✔
1434
        if (c->group)
388✔
1435
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1436

1437
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
775✔
1438

1439
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
388✔
1440

1441
        if (c->pam_name)
388✔
1442
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
1✔
1443

1444
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
388✔
1445
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
388✔
1446
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
388✔
1447
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
388✔
1448
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
388✔
1449
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
388✔
1450

1451
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
392✔
1452
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1453
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1454
                        mount->ignore_enoent ? "-": "",
4✔
1455
                        mount->source,
1456
                        mount->destination,
1457
                        mount->recursive ? "rbind" : "norbind");
4✔
1458

1459
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
388✔
1460
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1461
                        tmpfs->path,
1462
                        isempty(tmpfs->options) ? "" : ":",
×
1463
                        strempty(tmpfs->options));
×
1464

1465
        if (c->utmp_id)
388✔
1466
                fprintf(f,
2✔
1467
                        "%sUtmpIdentifier: %s\n",
1468
                        prefix, c->utmp_id);
1469

1470
        if (c->selinux_context)
388✔
1471
                fprintf(f,
×
1472
                        "%sSELinuxContext: %s%s\n",
1473
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1474

1475
        if (c->apparmor_profile)
388✔
1476
                fprintf(f,
×
1477
                        "%sAppArmorProfile: %s%s\n",
1478
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1479

1480
        if (c->smack_process_label)
388✔
1481
                fprintf(f,
×
1482
                        "%sSmackProcessLabel: %s%s\n",
1483
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1484

1485
        if (c->personality != PERSONALITY_INVALID)
388✔
1486
                fprintf(f,
1✔
1487
                        "%sPersonality: %s\n",
1488
                        prefix, strna(personality_to_string(c->personality)));
1489

1490
        fprintf(f,
388✔
1491
                "%sLockPersonality: %s\n",
1492
                prefix, yes_no(c->lock_personality));
388✔
1493

1494
        if (c->syscall_filter) {
388✔
1495
                fprintf(f,
24✔
1496
                        "%sSystemCallFilter: ",
1497
                        prefix);
1498

1499
                if (!c->syscall_allow_list)
24✔
1500
                        fputc('~', f);
×
1501

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

1511
                                if (first)
9,332✔
1512
                                        first = false;
1513
                                else
1514
                                        fputc(' ', f);
9,308✔
1515

1516
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
9,332✔
1517
                                fputs(strna(name), f);
9,332✔
1518

1519
                                if (num >= 0) {
9,332✔
1520
                                        errno_name = seccomp_errno_or_action_to_string(num);
×
1521
                                        if (errno_name)
×
1522
                                                fprintf(f, ":%s", errno_name);
×
1523
                                        else
1524
                                                fprintf(f, ":%d", num);
×
1525
                                }
1526
                        }
1527
                }
1528
#endif
1529

1530
                fputc('\n', f);
24✔
1531
        }
1532

1533
        if (c->syscall_archs) {
388✔
1534
                fprintf(f,
26✔
1535
                        "%sSystemCallArchitectures:",
1536
                        prefix);
1537

1538
#if HAVE_SECCOMP
1539
                void *id;
26✔
1540
                SET_FOREACH(id, c->syscall_archs)
52✔
1541
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
26✔
1542
#endif
1543
                fputc('\n', f);
26✔
1544
        }
1545

1546
        if (exec_context_restrict_namespaces_set(c)) {
388✔
1547
                _cleanup_free_ char *s = NULL;
22✔
1548

1549
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
22✔
1550
                if (r >= 0)
22✔
1551
                        fprintf(f, "%sRestrictNamespaces: %s\n",
22✔
1552
                                prefix, strna(s));
1553
        }
1554

1555
#if HAVE_LIBBPF
1556
        if (exec_context_restrict_filesystems_set(c)) {
388✔
1557
                char *fs;
×
1558
                SET_FOREACH(fs, c->restrict_filesystems)
×
1559
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1560
        }
1561
#endif
1562

1563
        if (c->user_namespace_path)
388✔
1564
                fprintf(f,
×
1565
                        "%sUserNamespacePath: %s\n",
1566
                        prefix, c->user_namespace_path);
1567

1568
        if (c->network_namespace_path)
388✔
1569
                fprintf(f,
×
1570
                        "%sNetworkNamespacePath: %s\n",
1571
                        prefix, c->network_namespace_path);
1572

1573
        if (c->syscall_errno > 0) {
388✔
1574
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
387✔
1575

1576
#if HAVE_SECCOMP
1577
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
387✔
1578
                if (errno_name)
387✔
1579
                        fputs(errno_name, f);
387✔
1580
                else
1581
                        fprintf(f, "%d", c->syscall_errno);
×
1582
#endif
1583
                fputc('\n', f);
387✔
1584
        }
1585

1586
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
388✔
1587
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1588
                        mount->ignore_enoent ? "-": "",
×
1589
                        mount->source,
1590
                        mount->destination);
1591
                if (mount->mount_options) {
×
1592
                        _cleanup_free_ char *opts = NULL;
×
1593

1594
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1595
                                fprintf(f, " %s", opts);
×
1596
                }
1597
                fprintf(f, "\n");
×
1598
        }
1599

1600
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
388✔
1601
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1602
                        mount->ignore_enoent ? "-": "",
×
1603
                        mount->source);
1604
                if (mount->mount_options) {
×
1605
                        _cleanup_free_ char *opts = NULL;
×
1606

1607
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1608
                                fprintf(f, " %s", opts);
×
1609
                }
1610
                fprintf(f, "\n");
×
1611
        }
1612

1613
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
388✔
1614
}
388✔
1615

1616
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1617
        assert(c);
×
1618

1619
        /* Returns true if the process forked off would run under
1620
         * an unchanged UID or as root. */
1621

1622
        if (!c->user)
×
1623
                return true;
1624

1625
        if (STR_IN_SET(c->user, "root", "0"))
×
1626
                return true;
×
1627

1628
        return false;
×
1629
}
1630

1631
int exec_context_get_effective_ioprio(const ExecContext *c) {
5,918✔
1632
        int p;
5,918✔
1633

1634
        assert(c);
5,918✔
1635

1636
        if (c->ioprio_is_set)
5,918✔
1637
                return c->ioprio;
14✔
1638

1639
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
5,904✔
1640
        if (p < 0)
5,904✔
1641
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1642

1643
        return ioprio_normalize(p);
5,904✔
1644
}
1645

1646
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
39,387✔
1647
        assert(c);
39,387✔
1648

1649
        /* Explicit setting wins */
1650
        if (c->mount_apivfs >= 0)
39,387✔
1651
                return c->mount_apivfs > 0;
119✔
1652

1653
        /* Default to "yes" if root directory or image are specified */
1654
        if (exec_context_with_rootfs(c))
39,268✔
1655
                return true;
251✔
1656

1657
        return false;
1658
}
1659

1660
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
31,944✔
1661
        assert(c);
31,944✔
1662

1663
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1664
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1665
        if (c->log_namespace)
31,944✔
1666
                return true;
1667

1668
        if (c->bind_log_sockets >= 0)
31,936✔
1669
                return c->bind_log_sockets > 0;
2✔
1670

1671
        if (exec_context_get_effective_mount_apivfs(c))
31,934✔
1672
                return true;
1673

1674
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1675
        if (exec_context_with_rootfs(c) && c->private_devices)
31,773✔
1676
                return true;
×
1677

1678
        return false;
1679
}
1680

1681
void exec_context_free_log_extra_fields(ExecContext *c) {
55,019✔
1682
        assert(c);
55,019✔
1683

1684
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
55,529✔
1685
                free(field->iov_base);
510✔
1686

1687
        c->log_extra_fields = mfree(c->log_extra_fields);
55,019✔
1688
        c->n_log_extra_fields = 0;
55,019✔
1689
}
55,019✔
1690

1691
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,686✔
1692
        _cleanup_close_ int fd = -EBADF;
1,686✔
1693
        const char *path;
1,686✔
1694
        struct stat st;
1,686✔
1695
        int r;
1,686✔
1696

1697
        assert(c);
1,686✔
1698

1699
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1700
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,686✔
1701

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

1708
        path = exec_context_tty_path(c);
35✔
1709
        if (!path)
35✔
1710
                return;
1711

1712
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
35✔
1713
        if (fd < 0)
35✔
1714
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1715
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1716
                                             path);
1717

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

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

1729
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
35✔
1730
        if (r < 0)
35✔
1731
                log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s to " UID_FMT ":" GID_FMT ", ignoring: %m", path, (uid_t) 0, (gid_t) TTY_GID);
35✔
1732
}
1733

1734
int exec_context_get_clean_directories(
1✔
1735
                ExecContext *c,
1736
                char **prefix,
1737
                ExecCleanMask mask,
1738
                char ***ret) {
1739

1740
        _cleanup_strv_free_ char **l = NULL;
1✔
1741
        int r;
1✔
1742

1743
        assert(c);
1✔
1744
        assert(prefix);
1✔
1745
        assert(ret);
1✔
1746

1747
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
6✔
1748
                if (!BIT_SET(mask, t))
5✔
1749
                        continue;
×
1750

1751
                if (!prefix[t])
5✔
1752
                        continue;
×
1753

1754
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
7✔
1755
                        char *j;
2✔
1756

1757
                        j = path_join(prefix[t], i->path);
2✔
1758
                        if (!j)
2✔
1759
                                return -ENOMEM;
1760

1761
                        r = strv_consume(&l, j);
2✔
1762
                        if (r < 0)
2✔
1763
                                return r;
1764

1765
                        /* Also remove private directories unconditionally. */
1766
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
2✔
1767
                                j = path_join(prefix[t], "private", i->path);
2✔
1768
                                if (!j)
2✔
1769
                                        return -ENOMEM;
1770

1771
                                r = strv_consume(&l, j);
2✔
1772
                                if (r < 0)
2✔
1773
                                        return r;
1774
                        }
1775

1776
                        STRV_FOREACH(symlink, i->symlinks) {
2✔
1777
                                j = path_join(prefix[t], *symlink);
×
1778
                                if (!j)
×
1779
                                        return -ENOMEM;
1780

1781
                                r = strv_consume(&l, j);
×
1782
                                if (r < 0)
×
1783
                                        return r;
1784
                        }
1785
                }
1786
        }
1787

1788
        *ret = TAKE_PTR(l);
1✔
1789
        return 0;
1✔
1790
}
1791

1792
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
2,943✔
1793
        ExecCleanMask mask = 0;
2,943✔
1794

1795
        assert(c);
2,943✔
1796
        assert(ret);
2,943✔
1797

1798
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
17,658✔
1799
                if (c->directories[t].n_items > 0)
14,715✔
1800
                        mask |= 1U << t;
309✔
1801

1802
        *ret = mask;
2,943✔
1803
        return 0;
2,943✔
1804
}
1805

1806
int exec_context_get_oom_score_adjust(const ExecContext *c) {
2,959✔
1807
        int n = 0, r;
2,959✔
1808

1809
        assert(c);
2,959✔
1810

1811
        if (c->oom_score_adjust_set)
2,959✔
1812
                return c->oom_score_adjust;
673✔
1813

1814
        r = get_oom_score_adjust(&n);
2,286✔
1815
        if (r < 0)
2,286✔
1816
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1817

1818
        return n;
2,286✔
1819
}
1820

1821
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
2,959✔
1822
        _cleanup_free_ char *t = NULL;
2,959✔
1823
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
2,959✔
1824
        int r;
2,959✔
1825

1826
        assert(c);
2,959✔
1827

1828
        if (c->coredump_filter_set)
2,959✔
1829
                return c->coredump_filter;
×
1830

1831
        r = read_one_line_file("/proc/self/coredump_filter", &t);
2,959✔
1832
        if (r < 0)
2,959✔
1833
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1834
        else {
1835
                r = safe_atoux64(t, &n);
2,959✔
1836
                if (r < 0)
2,959✔
1837
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1838
        }
1839

1840
        return n;
2,959✔
1841
}
1842

1843
int exec_context_get_nice(const ExecContext *c) {
2,959✔
1844
        int n;
2,959✔
1845

1846
        assert(c);
2,959✔
1847

1848
        if (c->nice_set)
2,959✔
1849
                return c->nice;
8✔
1850

1851
        errno = 0;
2,951✔
1852
        n = getpriority(PRIO_PROCESS, 0);
2,951✔
1853
        if (errno > 0) {
2,951✔
1854
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1855
                n = 0;
1856
        }
1857

1858
        return n;
1859
}
1860

1861
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
2,959✔
1862
        int n;
2,959✔
1863

1864
        assert(c);
2,959✔
1865

1866
        if (c->cpu_sched_set)
2,959✔
1867
                return c->cpu_sched_policy;
×
1868

1869
        n = sched_getscheduler(0);
2,959✔
1870
        if (n < 0)
2,959✔
1871
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1872

1873
        return n < 0 ? SCHED_OTHER : n;
2,959✔
1874
}
1875

1876
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
2,959✔
1877
        struct sched_param p = {};
2,959✔
1878
        int r;
2,959✔
1879

1880
        assert(c);
2,959✔
1881

1882
        if (c->cpu_sched_set)
2,959✔
1883
                return c->cpu_sched_priority;
×
1884

1885
        r = sched_getparam(0, &p);
2,959✔
1886
        if (r < 0)
2,959✔
1887
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1888

1889
        return r >= 0 ? p.sched_priority : 0;
2,959✔
1890
}
1891

1892
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
2,959✔
1893
        int r;
2,959✔
1894

1895
        assert(c);
2,959✔
1896

1897
        if (c->timer_slack_nsec != NSEC_INFINITY)
2,959✔
1898
                return c->timer_slack_nsec;
1899

1900
        r = prctl(PR_GET_TIMERSLACK);
2,959✔
1901
        if (r < 0)
2,959✔
1902
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1903

1904
        return (uint64_t) MAX(r, 0);
2,959✔
1905
}
1906

1907
bool exec_context_get_set_login_environment(const ExecContext *c) {
13,054✔
1908
        assert(c);
13,054✔
1909

1910
        if (c->set_login_environment >= 0)
13,054✔
1911
                return c->set_login_environment;
×
1912

1913
        return c->user || c->dynamic_user || c->pam_name;
22,669✔
1914
}
1915

1916
char** exec_context_get_syscall_filter(const ExecContext *c) {
2,959✔
1917
        _cleanup_strv_free_ char **l = NULL;
2,959✔
1918

1919
        assert(c);
2,959✔
1920

1921
#if HAVE_SECCOMP
1922
        if (dlopen_libseccomp() < 0)
2,959✔
1923
                return strv_new(NULL);
×
1924

1925
        void *id, *val;
2,959✔
1926
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
14,324✔
1927
                _cleanup_free_ char *name = NULL;
11,365✔
1928
                const char *e = NULL;
11,365✔
1929
                char *s;
11,365✔
1930
                int num = PTR_TO_INT(val);
11,365✔
1931

1932
                if (c->syscall_allow_list && num >= 0)
11,365✔
1933
                        /* syscall with num >= 0 in allow-list is denied. */
1934
                        continue;
×
1935

1936
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
11,365✔
1937
                if (!name)
11,365✔
1938
                        continue;
×
1939

1940
                if (num >= 0) {
11,365✔
1941
                        e = seccomp_errno_or_action_to_string(num);
×
1942
                        if (e) {
×
1943
                                s = strjoin(name, ":", e);
×
1944
                                if (!s)
×
1945
                                        return NULL;
1946
                        } else {
1947
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1948
                                        return NULL;
1949
                        }
1950
                } else
1951
                        s = TAKE_PTR(name);
11,365✔
1952

1953
                if (strv_consume(&l, s) < 0)
11,365✔
1954
                        return NULL;
1955
        }
1956

1957
        strv_sort(l);
2,959✔
1958
#endif
1959

1960
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,959✔
1961
}
1962

1963
char** exec_context_get_syscall_archs(const ExecContext *c) {
2,959✔
1964
        _cleanup_strv_free_ char **l = NULL;
2,959✔
1965

1966
        assert(c);
2,959✔
1967

1968
#if HAVE_SECCOMP
1969
        void *id;
2,959✔
1970
        SET_FOREACH(id, c->syscall_archs) {
2,992✔
1971
                const char *name;
33✔
1972

1973
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
33✔
1974
                if (!name)
33✔
1975
                        continue;
×
1976

1977
                if (strv_extend(&l, name) < 0)
33✔
1978
                        return NULL;
×
1979
        }
1980

1981
        strv_sort(l);
2,959✔
1982
#endif
1983

1984
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,959✔
1985
}
1986

1987
char** exec_context_get_syscall_log(const ExecContext *c) {
2,959✔
1988
        _cleanup_strv_free_ char **l = NULL;
2,959✔
1989

1990
        assert(c);
2,959✔
1991

1992
#if HAVE_SECCOMP
1993
        if (dlopen_libseccomp() < 0)
2,959✔
1994
                return strv_new(NULL);
×
1995

1996
        void *id, *val;
2,959✔
1997
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
2,959✔
1998
                char *name = NULL;
×
1999

2000
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
2001
                if (!name)
×
2002
                        continue;
×
2003

2004
                if (strv_consume(&l, name) < 0)
×
2005
                        return NULL;
×
2006
        }
2007

2008
        strv_sort(l);
2,959✔
2009
#endif
2010

2011
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,959✔
2012
}
2013

2014
char** exec_context_get_address_families(const ExecContext *c) {
2,959✔
2015
        _cleanup_strv_free_ char **l = NULL;
2,959✔
2016
        void *af;
2,959✔
2017

2018
        assert(c);
2,959✔
2019

2020
        SET_FOREACH(af, c->address_families) {
3,065✔
2021
                const char *name;
106✔
2022

2023
                name = af_to_name(PTR_TO_INT(af));
106✔
2024
                if (!name)
106✔
2025
                        continue;
×
2026

2027
                if (strv_extend(&l, name) < 0)
106✔
2028
                        return NULL;
×
2029
        }
2030

2031
        strv_sort(l);
2,959✔
2032

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

2036
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
2,959✔
2037
        assert(c);
2,959✔
2038

2039
#if HAVE_LIBBPF
2040
        char **l = set_get_strv(c->restrict_filesystems);
2,959✔
2041
        if (!l)
2,959✔
2042
                return NULL;
2043

2044
        return strv_sort(l);
2,959✔
2045
#else
2046
        return strv_new(NULL);
2047
#endif
2048
}
2049

2050
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,701✔
2051
        assert(c);
13,701✔
2052

2053
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,701✔
2054
}
2055

2056
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
15,454✔
2057
        assert(c);
15,454✔
2058

2059
        return c->restrict_filesystems_allow_list ||
15,454✔
2060
          !set_isempty(c->restrict_filesystems);
15,454✔
2061
}
2062

2063
bool exec_context_with_rootfs(const ExecContext *c) {
74,536✔
2064
        assert(c);
74,536✔
2065

2066
        /* Checks if RootDirectory=, RootImage=, RootMStack= or RootDirectoryFileDescriptor= are used */
2067

2068
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd || c->root_mstack;
74,536✔
2069
}
2070

2071
bool exec_context_with_rootfs_strict(const ExecContext *c) {
1,404✔
2072
        assert(c);
1,404✔
2073

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

2078
        return c->root_directory || c->root_image || c->root_directory_as_fd || c->root_mstack;
1,404✔
2079
}
2080

2081
int exec_context_has_vpicked_extensions(const ExecContext *context) {
14,548✔
2082
        int r;
14,548✔
2083

2084
        assert(context);
14,548✔
2085

2086
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
14,604✔
2087
                r = path_uses_vpick(mi->source);
56✔
2088
                if (r != 0)
56✔
2089
                        return r;
2090
        }
2091
        STRV_FOREACH(ed, context->extension_directories) {
14,549✔
2092
                r = path_uses_vpick(*ed);
1✔
2093
                if (r != 0)
1✔
2094
                        return r;
2095
        }
2096

2097
        return 0;
2098
}
2099

2100
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
5,059✔
2101
        assert(s);
5,059✔
2102

2103
        *s = (ExecStatus) {
5,059✔
2104
                .pid = pid,
2105
        };
2106

2107
        if (ts)
5,059✔
2108
                s->start_timestamp = *ts;
5,059✔
2109
        else
2110
                dual_timestamp_now(&s->start_timestamp);
×
2111
}
5,059✔
2112

2113
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,297✔
2114
        assert(s);
2,297✔
2115

2116
        if (s->pid != pid)
2,297✔
2117
                *s = (ExecStatus) {
2✔
2118
                        .pid = pid,
2119
                };
2120

2121
        dual_timestamp_now(&s->exit_timestamp);
2,297✔
2122

2123
        s->code = code;
2,297✔
2124
        s->status = status;
2,297✔
2125

2126
        if (context && context->utmp_id)
2,297✔
2127
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2128
}
2,297✔
2129

2130
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
8,484✔
2131
        assert(s);
8,484✔
2132
        assert(ucred);
8,484✔
2133
        assert(ts);
8,484✔
2134

2135
        if (ucred->pid != s->pid)
8,484✔
2136
                *s = (ExecStatus) {
7✔
2137
                        .pid = ucred->pid,
2138
                };
2139

2140
        s->handoff_timestamp = *ts;
8,484✔
2141
}
8,484✔
2142

2143
void exec_status_reset(ExecStatus *s) {
27,693✔
2144
        assert(s);
27,693✔
2145

2146
        *s = (ExecStatus) {};
27,693✔
2147
}
27,693✔
2148

2149
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
225✔
2150
        assert(s);
225✔
2151
        assert(f);
225✔
2152

2153
        if (s->pid <= 0)
225✔
2154
                return;
2155

2156
        prefix = strempty(prefix);
43✔
2157

2158
        fprintf(f,
43✔
2159
                "%sPID: "PID_FMT"\n",
2160
                prefix, s->pid);
2161

2162
        if (dual_timestamp_is_set(&s->start_timestamp))
43✔
2163
                fprintf(f,
43✔
2164
                        "%sStart Timestamp: %s\n",
2165
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
43✔
2166

2167
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
43✔
2168
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
42✔
2169
                fprintf(f,
42✔
2170
                        "%sHandoff Timestamp: %s since start\n",
2171
                        prefix,
2172
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
84✔
2173
        else
2174
                fprintf(f,
1✔
2175
                        "%sHandoff Timestamp: %s\n",
2176
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
1✔
2177

2178
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
43✔
2179

2180
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
21✔
2181
                        fprintf(f,
21✔
2182
                                "%sExit Timestamp: %s since handoff\n",
2183
                                prefix,
2184
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
42✔
2185
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2186
                        fprintf(f,
×
2187
                                "%sExit Timestamp: %s since start\n",
2188
                                prefix,
2189
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2190
                else
2191
                        fprintf(f,
×
2192
                                "%sExit Timestamp: %s\n",
2193
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2194

2195
                fprintf(f,
21✔
2196
                        "%sExit Code: %s\n"
2197
                        "%sExit Status: %i\n",
2198
                        prefix, sigchld_code_to_string(s->code),
21✔
2199
                        prefix, s->status);
21✔
2200
        }
2201
}
2202

2203
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
225✔
2204
        _cleanup_free_ char *cmd = NULL;
450✔
2205
        const char *prefix2;
225✔
2206

2207
        assert(c);
225✔
2208
        assert(f);
225✔
2209

2210
        prefix = strempty(prefix);
225✔
2211
        prefix2 = strjoina(prefix, "\t");
1,125✔
2212

2213
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
225✔
2214

2215
        fprintf(f,
225✔
2216
                "%sCommand Line: %s\n",
2217
                prefix, strnull(cmd));
2218

2219
        exec_status_dump(&c->exec_status, f, prefix2);
225✔
2220
}
225✔
2221

2222
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
220✔
2223
        assert(f);
220✔
2224

2225
        prefix = strempty(prefix);
220✔
2226

2227
        LIST_FOREACH(command, i, c)
444✔
2228
                exec_command_dump(i, f, prefix);
224✔
2229
}
220✔
2230

2231
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
18,098✔
2232
        ExecCommand *end;
18,098✔
2233

2234
        assert(l);
18,098✔
2235
        assert(e);
18,098✔
2236

2237
        if (*l) {
18,098✔
2238
                /* It's kind of important, that we keep the order here */
2239
                end = LIST_FIND_TAIL(command, *l);
370✔
2240
                LIST_INSERT_AFTER(command, *l, end, e);
133✔
2241
        } else
2242
                *l = e;
17,965✔
2243
}
18,098✔
2244

2245
int exec_command_set(ExecCommand *c, const char *path, ...) {
204✔
2246
        va_list ap;
204✔
2247
        char **l, *p;
204✔
2248

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

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

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

2259
        p = strdup(path);
204✔
2260
        if (!p) {
204✔
2261
                strv_free(l);
×
2262
                return -ENOMEM;
×
2263
        }
2264

2265
        free_and_replace(c->path, p);
204✔
2266

2267
        return strv_free_and_replace(c->argv, l);
204✔
2268
}
2269

2270
int exec_command_append(ExecCommand *c, const char *path, ...) {
292✔
2271
        char **l;
292✔
2272
        va_list ap;
292✔
2273
        int r;
292✔
2274

2275
        assert(c);
292✔
2276
        assert(path);
292✔
2277

2278
        va_start(ap, path);
292✔
2279
        l = strv_new_ap(path, ap);
292✔
2280
        va_end(ap);
292✔
2281

2282
        if (!l)
292✔
2283
                return -ENOMEM;
292✔
2284

2285
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates= */ false);
292✔
2286
        if (r < 0)
292✔
2287
                return r;
×
2288

2289
        return 0;
2290
}
2291

2292
static char *destroy_tree(char *path) {
272✔
2293
        if (!path)
272✔
2294
                return NULL;
2295

2296
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
90✔
2297
                log_debug("Spawning process to nuke '%s'", path);
90✔
2298

2299
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
90✔
2300
        }
2301

2302
        return mfree(path);
90✔
2303
}
2304

2305
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
132,082✔
2306
        assert(rt);
132,082✔
2307

2308
        if (rt->manager)
132,082✔
2309
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
167✔
2310

2311
        rt->id = mfree(rt->id);
132,082✔
2312
        rt->tmp_dir = mfree(rt->tmp_dir);
132,082✔
2313
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
132,082✔
2314
        safe_close_pair(rt->userns_storage_socket);
132,082✔
2315
        safe_close_pair(rt->netns_storage_socket);
132,082✔
2316
        safe_close_pair(rt->ipcns_storage_socket);
132,082✔
2317
}
132,082✔
2318

2319
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
132,014✔
2320
        if (!rt)
132,014✔
2321
                return NULL;
2322

2323
        exec_shared_runtime_done(rt);
132,014✔
2324
        return mfree(rt);
132,014✔
2325
}
2326

2327
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
170✔
2328
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
134,892✔
2329

2330
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
54✔
2331
        if (!rt)
54✔
2332
                return NULL;
2333

2334
        assert(rt->n_ref > 0);
51✔
2335
        rt->n_ref--;
51✔
2336

2337
        if (rt->n_ref > 0)
51✔
2338
                return NULL;
2339

2340
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
51✔
2341
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
51✔
2342

2343
        return exec_shared_runtime_free(rt);
51✔
2344
}
2345

2346
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
132,014✔
2347
        _cleanup_free_ char *id_copy = NULL;
264,028✔
2348
        ExecSharedRuntime *n;
132,014✔
2349

2350
        assert(ret);
132,014✔
2351

2352
        id_copy = strdup(id);
132,014✔
2353
        if (!id_copy)
132,014✔
2354
                return -ENOMEM;
2355

2356
        n = new(ExecSharedRuntime, 1);
132,014✔
2357
        if (!n)
132,014✔
2358
                return -ENOMEM;
2359

2360
        *n = (ExecSharedRuntime) {
132,014✔
2361
                .id = TAKE_PTR(id_copy),
132,014✔
2362
                .userns_storage_socket = EBADF_PAIR,
2363
                .netns_storage_socket = EBADF_PAIR,
2364
                .ipcns_storage_socket = EBADF_PAIR,
2365
        };
2366

2367
        *ret = n;
132,014✔
2368
        return 0;
132,014✔
2369
}
2370

2371
static int exec_shared_runtime_add(
167✔
2372
                Manager *m,
2373
                const char *id,
2374
                char **tmp_dir,
2375
                char **var_tmp_dir,
2376
                int userns_storage_socket[2],
2377
                int netns_storage_socket[2],
2378
                int ipcns_storage_socket[2],
2379
                ExecSharedRuntime **ret) {
2380

2381
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
167✔
2382
        int r;
167✔
2383

2384
        assert(m);
167✔
2385
        assert(id);
167✔
2386

2387
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2388

2389
        r = exec_shared_runtime_allocate(&rt, id);
167✔
2390
        if (r < 0)
167✔
2391
                return r;
2392

2393
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
167✔
2394
        if (r < 0)
167✔
2395
                return r;
2396

2397
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
167✔
2398
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
167✔
2399
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
167✔
2400

2401
        if (userns_storage_socket) {
167✔
2402
                rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
167✔
2403
                rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
167✔
2404
        }
2405

2406
        if (netns_storage_socket) {
167✔
2407
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
167✔
2408
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
167✔
2409
        }
2410

2411
        if (ipcns_storage_socket) {
167✔
2412
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
167✔
2413
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
167✔
2414
        }
2415

2416
        rt->manager = m;
167✔
2417

2418
        if (ret)
167✔
2419
                *ret = rt;
75✔
2420
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2421
        TAKE_PTR(rt);
167✔
2422
        return 0;
167✔
2423
}
2424

2425
static int exec_shared_runtime_make(
7,346✔
2426
                Manager *m,
2427
                const ExecContext *c,
2428
                const char *id,
2429
                ExecSharedRuntime **ret) {
2430

2431
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
7,346✔
2432
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
22,038✔
2433
        int r;
7,346✔
2434

2435
        assert(m);
7,346✔
2436
        assert(c);
7,346✔
2437
        assert(id);
7,346✔
2438

2439
        /* It is not necessary to create ExecSharedRuntime object. */
2440
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
7,346✔
2441
                *ret = NULL;
7,271✔
2442
                return 0;
7,271✔
2443
        }
2444

2445
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
144✔
2446
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
69✔
2447
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2448
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2449
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
69✔
2450
                if (r < 0)
69✔
2451
                        return r;
2452
        }
2453

2454
        if (c->user_namespace_path)
75✔
2455
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2456
                        return -errno;
×
2457

2458
        if (exec_needs_network_namespace(c))
75✔
2459
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
8✔
2460
                        return -errno;
×
2461

2462
        if (exec_needs_ipc_namespace(c))
75✔
2463
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2464
                        return -errno;
×
2465

2466
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
75✔
2467
        if (r < 0)
75✔
2468
                return r;
×
2469

2470
        return 1;
2471
}
2472

2473
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
7,438✔
2474
        ExecSharedRuntime *rt;
7,438✔
2475
        int r;
7,438✔
2476

2477
        assert(m);
7,438✔
2478
        assert(id);
7,438✔
2479
        assert(ret);
7,438✔
2480

2481
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
7,438✔
2482
        if (rt)
7,438✔
2483
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2484
                goto ref;
92✔
2485

2486
        if (!create) {
7,346✔
2487
                *ret = NULL;
×
2488
                return 0;
×
2489
        }
2490

2491
        /* If not found, then create a new object. */
2492
        r = exec_shared_runtime_make(m, c, id, &rt);
7,346✔
2493
        if (r < 0)
7,346✔
2494
                return r;
2495
        if (r == 0) {
7,346✔
2496
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2497
                *ret = NULL;
7,271✔
2498
                return 0;
7,271✔
2499
        }
2500

2501
ref:
75✔
2502
        /* increment reference counter. */
2503
        rt->n_ref++;
167✔
2504
        *ret = rt;
167✔
2505
        return 1;
167✔
2506
}
2507

2508
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
100✔
2509
        ExecSharedRuntime *rt;
100✔
2510

2511
        assert(m);
100✔
2512
        assert(f);
100✔
2513
        assert(fds);
100✔
2514

2515
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
216✔
2516
                fprintf(f, "exec-runtime=%s", rt->id);
116✔
2517

2518
                if (rt->tmp_dir)
116✔
2519
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
116✔
2520

2521
                if (rt->var_tmp_dir)
116✔
2522
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
116✔
2523

2524
                if (rt->userns_storage_socket[0] >= 0) {
116✔
2525
                        int copy;
×
2526

2527
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
2528
                        if (copy < 0)
×
2529
                                return copy;
×
2530

2531
                        fprintf(f, " userns-socket-0=%i", copy);
×
2532
                }
2533

2534
                if (rt->userns_storage_socket[1] >= 0) {
116✔
2535
                        int copy;
×
2536

2537
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
2538
                        if (copy < 0)
×
2539
                                return copy;
2540

2541
                        fprintf(f, " userns-socket-1=%i", copy);
×
2542
                }
2543

2544
                if (rt->netns_storage_socket[0] >= 0) {
116✔
2545
                        int copy;
4✔
2546

2547
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
4✔
2548
                        if (copy < 0)
4✔
2549
                                return copy;
2550

2551
                        fprintf(f, " netns-socket-0=%i", copy);
4✔
2552
                }
2553

2554
                if (rt->netns_storage_socket[1] >= 0) {
116✔
2555
                        int copy;
4✔
2556

2557
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
4✔
2558
                        if (copy < 0)
4✔
2559
                                return copy;
2560

2561
                        fprintf(f, " netns-socket-1=%i", copy);
4✔
2562
                }
2563

2564
                if (rt->ipcns_storage_socket[0] >= 0) {
116✔
2565
                        int copy;
×
2566

2567
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2568
                        if (copy < 0)
×
2569
                                return copy;
2570

2571
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2572
                }
2573

2574
                if (rt->ipcns_storage_socket[1] >= 0) {
116✔
2575
                        int copy;
×
2576

2577
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2578
                        if (copy < 0)
×
2579
                                return copy;
2580

2581
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2582
                }
2583

2584
                fputc('\n', f);
116✔
2585
        }
2586

2587
        return 0;
100✔
2588
}
2589

2590
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
134,725✔
2591
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
134,725✔
2592
        ExecSharedRuntime *rt = NULL;
134,725✔
2593
        int r;
134,725✔
2594

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

2600
        assert(u);
134,725✔
2601
        assert(key);
134,725✔
2602
        assert(value);
134,725✔
2603

2604
        /* Manager manages ExecSharedRuntime objects by the unit id.
2605
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2606
        if (isempty(u->id)) {
134,725✔
2607
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2608
                return 0;
×
2609
        }
2610

2611
        if (u->manager) {
134,725✔
2612
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
134,725✔
2613
                        return log_oom();
×
2614

2615
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
134,725✔
2616
        }
2617
        if (!rt) {
134,725✔
2618
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
131,847✔
2619
                        return log_oom();
×
2620

2621
                rt = rt_create;
131,847✔
2622
        }
2623

2624
        if (streq(key, "tmp-dir")) {
134,725✔
2625
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2626
                        return -ENOMEM;
2627

2628
        } else if (streq(key, "var-tmp-dir")) {
134,725✔
2629
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2630
                        return -ENOMEM;
2631

2632
        } else if (streq(key, "netns-socket-0")) {
134,725✔
2633

2634
                safe_close(rt->netns_storage_socket[0]);
×
2635
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2636
                if (rt->netns_storage_socket[0] < 0)
×
2637
                        return 0;
2638

2639
        } else if (streq(key, "netns-socket-1")) {
134,725✔
2640

2641
                safe_close(rt->netns_storage_socket[1]);
×
2642
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2643
                if (rt->netns_storage_socket[1] < 0)
×
2644
                        return 0;
2645
        } else
2646
                return 0;
2647

2648
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2649
        if (rt_create && u->manager) {
×
2650
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2651
                if (r < 0) {
×
2652
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2653
                        return 0;
×
2654
                }
2655

2656
                rt_create->manager = u->manager;
×
2657

2658
                /* Avoid cleanup */
2659
                TAKE_PTR(rt_create);
×
2660
        }
2661

2662
        return 1;
2663
}
2664

2665
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
92✔
2666
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
92✔
2667
        char *id = NULL;
92✔
2668
        int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
92✔
2669
        const char *p, *v = ASSERT_PTR(value);
92✔
2670
        size_t n;
92✔
2671

2672
        assert(m);
92✔
2673
        assert(fds);
92✔
2674

2675
        n = strcspn(v, " ");
92✔
2676
        id = strndupa_safe(v, n);
92✔
2677
        if (v[n] != ' ')
92✔
2678
                goto finalize;
×
2679
        p = v + n + 1;
92✔
2680

2681
        v = startswith(p, "tmp-dir=");
92✔
2682
        if (v) {
92✔
2683
                n = strcspn(v, " ");
92✔
2684
                tmp_dir = strndup(v, n);
92✔
2685
                if (!tmp_dir)
92✔
2686
                        return log_oom();
×
2687
                if (v[n] != ' ')
92✔
2688
                        goto finalize;
×
2689
                p = v + n + 1;
92✔
2690
        }
2691

2692
        v = startswith(p, "var-tmp-dir=");
92✔
2693
        if (v) {
92✔
2694
                n = strcspn(v, " ");
92✔
2695
                var_tmp_dir = strndup(v, n);
92✔
2696
                if (!var_tmp_dir)
92✔
2697
                        return log_oom();
×
2698
                if (v[n] != ' ')
92✔
2699
                        goto finalize;
90✔
2700
                p = v + n + 1;
2✔
2701
        }
2702

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

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

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

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

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

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

2733
        v = startswith(p, "netns-socket-0=");
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[0] = deserialize_fd(fds, buf);
2✔
2741
                if (netns_fdpair[0] < 0)
2✔
2742
                        return netns_fdpair[0];
2743
                if (v[n] != ' ')
2✔
2744
                        goto finalize;
×
2745
                p = v + n + 1;
2✔
2746
        }
2747

2748
        v = startswith(p, "netns-socket-1=");
2✔
2749
        if (v) {
2✔
2750
                char *buf;
2✔
2751

2752
                n = strcspn(v, " ");
2✔
2753
                buf = strndupa_safe(v, n);
2✔
2754

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

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

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

2770
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2771
                if (ipcns_fdpair[0] < 0)
×
2772
                        return ipcns_fdpair[0];
2773
                if (v[n] != ' ')
×
2774
                        goto finalize;
×
2775
                p = v + n + 1;
×
2776
        }
2777

2778
        v = startswith(p, "ipcns-socket-1=");
×
2779
        if (v) {
×
2780
                char *buf;
×
2781

2782
                n = strcspn(v, " ");
×
2783
                buf = strndupa_safe(v, n);
×
2784

2785
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2786
                if (ipcns_fdpair[1] < 0)
×
2787
                        return ipcns_fdpair[1];
2788
        }
2789

2790
finalize:
×
2791
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
92✔
2792
        if (r < 0)
92✔
2793
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2794
        return 0;
2795
}
2796

2797
void exec_shared_runtime_vacuum(Manager *m) {
1,612✔
2798
        ExecSharedRuntime *rt;
1,612✔
2799

2800
        assert(m);
1,612✔
2801

2802
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2803

2804
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,704✔
2805
                if (rt->n_ref > 0)
92✔
2806
                        continue;
92✔
2807

2808
                (void) exec_shared_runtime_free(rt);
×
2809
        }
2810
}
1,612✔
2811

2812
int exec_runtime_make(
7,438✔
2813
                const Unit *unit,
2814
                const ExecContext *context,
2815
                ExecSharedRuntime *shared,
2816
                DynamicCreds *creds,
2817
                ExecRuntime **ret) {
2818
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
7,438✔
2819
        _cleanup_free_ char *ephemeral = NULL;
7,438✔
2820
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
7,438✔
2821
        int r;
7,438✔
2822

2823
        assert(unit);
7,438✔
2824
        assert(context);
7,438✔
2825
        assert(ret);
7,438✔
2826

2827
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
7,438✔
2828
                *ret = NULL;
7,268✔
2829
                return 0;
7,268✔
2830
        }
2831

2832
        if (exec_needs_ephemeral(context)) {
170✔
2833
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2834
                if (r < 0)
×
2835
                        return r;
2836

2837
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2838
                if (r < 0)
×
2839
                        return r;
2840

2841
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2842
                        return -errno;
×
2843
        }
2844

2845
        rt = new(ExecRuntime, 1);
170✔
2846
        if (!rt)
170✔
2847
                return -ENOMEM;
2848

2849
        *rt = (ExecRuntime) {
170✔
2850
                .shared = shared,
2851
                .dynamic_creds = creds,
2852
                .ephemeral_copy = TAKE_PTR(ephemeral),
170✔
2853
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
170✔
2854
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
170✔
2855
        };
2856

2857
        *ret = TAKE_PTR(rt);
170✔
2858
        return 1;
170✔
2859
}
2860

2861
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
55,003✔
2862
        if (!rt)
55,003✔
2863
                return NULL;
2864

2865
        exec_shared_runtime_unref(rt->shared);
170✔
2866
        dynamic_creds_unref(rt->dynamic_creds);
170✔
2867

2868
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
170✔
2869

2870
        safe_close_pair(rt->ephemeral_storage_socket);
170✔
2871
        return mfree(rt);
170✔
2872
}
2873

2874
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
6,352✔
2875
        if (!rt)
6,352✔
2876
                return NULL;
2877

2878
        rt->shared = exec_shared_runtime_destroy(rt->shared);
54✔
2879
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
54✔
2880
        return exec_runtime_free(rt);
54✔
2881
}
2882

2883
void exec_runtime_clear(ExecRuntime *rt) {
68✔
2884
        if (!rt)
68✔
2885
                return;
2886

2887
        safe_close_pair(rt->ephemeral_storage_socket);
68✔
2888
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
68✔
2889
}
2890

2891
void exec_params_shallow_clear(ExecParameters *p) {
2,549✔
2892
        if (!p)
2,549✔
2893
                return;
2894

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

2898
        p->environment = strv_free(p->environment);
2,549✔
2899
        p->fd_names = strv_free(p->fd_names);
2,549✔
2900
        p->files_env = strv_free(p->files_env);
2,549✔
2901
        p->fds = mfree(p->fds);
2,549✔
2902
        p->root_directory_fd = safe_close(p->root_directory_fd);
2,549✔
2903
        p->exec_fd = safe_close(p->exec_fd);
2,549✔
2904
        p->user_lookup_fd = -EBADF;
2,549✔
2905
        p->bpf_restrict_fs_map_fd = -EBADF;
2,549✔
2906
        p->unit_id = mfree(p->unit_id);
2,549✔
2907
        p->invocation_id = SD_ID128_NULL;
2,549✔
2908
        p->invocation_id_string[0] = '\0';
2,549✔
2909
        p->confirm_spawn = mfree(p->confirm_spawn);
2,549✔
2910
}
2911

2912
void exec_params_deep_clear(ExecParameters *p) {
68✔
2913
        if (!p)
68✔
2914
                return;
2915

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

2920
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
68✔
2921

2922
        p->cgroup_path = mfree(p->cgroup_path);
68✔
2923

2924
        if (p->prefix) {
68✔
2925
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
68✔
2926
                p->prefix = mfree(p->prefix);
68✔
2927
        }
2928

2929
        p->received_credentials_directory = mfree(p->received_credentials_directory);
68✔
2930
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
68✔
2931

2932
        if (p->idle_pipe) {
68✔
2933
                close_many_and_free(p->idle_pipe, 4);
34✔
2934
                p->idle_pipe = NULL;
34✔
2935
        }
2936

2937
        p->stdin_fd = safe_close(p->stdin_fd);
68✔
2938
        p->stdout_fd = safe_close(p->stdout_fd);
68✔
2939
        p->stderr_fd = safe_close(p->stderr_fd);
68✔
2940
        p->root_directory_fd = safe_close(p->root_directory_fd);
68✔
2941

2942
        p->notify_socket = mfree(p->notify_socket);
68✔
2943

2944
        open_file_free_many(&p->open_files);
68✔
2945

2946
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
68✔
2947

2948
        exec_params_shallow_clear(p);
68✔
2949
}
2950

2951
void exec_directory_done(ExecDirectory *d) {
275,085✔
2952
        if (!d)
275,085✔
2953
                return;
2954

2955
        FOREACH_ARRAY(i, d->items, d->n_items) {
276,780✔
2956
                free(i->path);
1,695✔
2957
                strv_free(i->symlinks);
1,695✔
2958
        }
2959

2960
        d->items = mfree(d->items);
275,085✔
2961
        d->n_items = 0;
275,085✔
2962
        d->mode = 0755;
275,085✔
2963
}
2964

2965
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
4,670✔
2966
        assert(d);
4,670✔
2967
        assert(path);
4,670✔
2968

2969
        FOREACH_ARRAY(i, d->items, d->n_items)
7,084✔
2970
                if (path_equal(i->path, path))
2,429✔
2971
                        return i;
2972

2973
        return NULL;
2974
}
2975

2976
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
4,670✔
2977
        _cleanup_strv_free_ char **s = NULL;
×
2978
        _cleanup_free_ char *p = NULL;
4,670✔
2979
        ExecDirectoryItem *existing;
4,670✔
2980
        int r;
4,670✔
2981

2982
        assert(d);
4,670✔
2983
        assert(path);
4,670✔
2984

2985
        existing = exec_directory_find(d, path);
4,670✔
2986
        if (existing) {
4,670✔
2987
                r = strv_extend(&existing->symlinks, symlink);
15✔
2988
                if (r < 0)
15✔
2989
                        return r;
2990

2991
                existing->flags |= flags;
15✔
2992

2993
                return 0; /* existing item is updated */
15✔
2994
        }
2995

2996
        p = strdup(path);
4,655✔
2997
        if (!p)
4,655✔
2998
                return -ENOMEM;
2999

3000
        if (symlink) {
4,655✔
3001
                s = strv_new(symlink);
6✔
3002
                if (!s)
6✔
3003
                        return -ENOMEM;
3004
        }
3005

3006
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
4,655✔
3007
                return -ENOMEM;
3008

3009
        d->items[d->n_items++] = (ExecDirectoryItem) {
4,655✔
3010
                .path = TAKE_PTR(p),
4,655✔
3011
                .symlinks = TAKE_PTR(s),
4,655✔
3012
                .flags = flags,
3013
        };
3014

3015
        return 1; /* new item is added */
4,655✔
3016
}
3017

3018
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
911✔
3019
        assert(a);
911✔
3020
        assert(b);
911✔
3021

3022
        return path_compare(a->path, b->path);
911✔
3023
}
3024

3025
void exec_directory_sort(ExecDirectory *d) {
149,601✔
3026
        assert(d);
149,601✔
3027

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

3033
        if (d->n_items <= 1)
149,601✔
3034
                return;
3035

3036
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
163✔
3037

3038
        for (size_t i = 1; i < d->n_items; i++)
710✔
3039
                for (size_t j = 0; j < i; j++)
1,822✔
3040
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,290✔
3041
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3042
                                break;
15✔
3043
                        }
3044
}
3045

3046
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3047
        ExecDirectoryType t;
4✔
3048

3049
        assert(s);
4✔
3050

3051
        if (streq(s, "all"))
4✔
3052
                return EXEC_CLEAN_ALL;
3053
        if (streq(s, "fdstore"))
3✔
3054
                return EXEC_CLEAN_FDSTORE;
3055

3056
        t = exec_resource_type_from_string(s);
2✔
3057
        if (t < 0)
2✔
3058
                return (ExecCleanMask) t;
3059

3060
        return 1U << t;
2✔
3061
}
3062

3063
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
3064
        [EXEC_INPUT_NULL]      = "null",
3065
        [EXEC_INPUT_TTY]       = "tty",
3066
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
3067
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
3068
        [EXEC_INPUT_SOCKET]    = "socket",
3069
        [EXEC_INPUT_NAMED_FD]  = "fd",
3070
        [EXEC_INPUT_DATA]      = "data",
3071
        [EXEC_INPUT_FILE]      = "file",
3072
};
3073

3074
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,609✔
3075

3076
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
3077
        [EXEC_OUTPUT_INHERIT]             = "inherit",
3078
        [EXEC_OUTPUT_NULL]                = "null",
3079
        [EXEC_OUTPUT_TTY]                 = "tty",
3080
        [EXEC_OUTPUT_KMSG]                = "kmsg",
3081
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
3082
        [EXEC_OUTPUT_JOURNAL]             = "journal",
3083
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
3084
        [EXEC_OUTPUT_SOCKET]              = "socket",
3085
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
3086
        [EXEC_OUTPUT_FILE]                = "file",
3087
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
3088
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
3089
};
3090

3091
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,465✔
3092

3093
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3094
        [EXEC_UTMP_INIT]  = "init",
3095
        [EXEC_UTMP_LOGIN] = "login",
3096
        [EXEC_UTMP_USER]  = "user",
3097
};
3098

3099
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,673✔
3100

3101
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3102
        [EXEC_PRESERVE_NO]      = "no",
3103
        [EXEC_PRESERVE_YES]     = "yes",
3104
        [EXEC_PRESERVE_RESTART] = "restart",
3105
};
3106

3107
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
19,548✔
3108

3109
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3110
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3111
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3112
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3113
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3114
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3115
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3116
};
3117

3118
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3119

3120
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3121
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3122
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3123
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3124
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3125
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3126
};
3127

3128
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3129

3130
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
3131
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
3132
 * directories, specifically .timer units with their timestamp touch file. */
3133
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3134
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
3135
        [EXEC_DIRECTORY_STATE]         = "state",
3136
        [EXEC_DIRECTORY_CACHE]         = "cache",
3137
        [EXEC_DIRECTORY_LOGS]          = "logs",
3138
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3139
};
3140

3141
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
315✔
3142

3143
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3144
        [EXEC_KEYRING_INHERIT] = "inherit",
3145
        [EXEC_KEYRING_PRIVATE] = "private",
3146
        [EXEC_KEYRING_SHARED]  = "shared",
3147
};
3148

3149
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
16,197✔
3150

3151
static const char* const memory_thp_table[_MEMORY_THP_MAX] = {
3152
        [MEMORY_THP_INHERIT] = "inherit",
3153
        [MEMORY_THP_DISABLE] = "disable",
3154
        [MEMORY_THP_MADVISE] = "madvise",
3155
        [MEMORY_THP_SYSTEM]  = "system",
3156
};
3157

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