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

systemd / systemd / 22601885815

02 Mar 2026 11:59PM UTC coverage: 72.342% (-0.2%) from 72.572%
22601885815

push

github

yuwata
network: fix error aggregation in wwan_check_and_set_configuration()

When removing marked routes, the condition `if (ret)` incorrectly
overwrites any previously accumulated error in `ret` with the latest
return value `r`, even if `r >= 0` (success). This means an earlier
real error can be silently cleared by a subsequent successful
route_remove() call.

The parallel address_remove() block just above uses the correct
`if (r < 0)` pattern. Apply the same fix to the route_remove() block.

0 of 1 new or added line in 1 file covered. (0.0%)

1949 existing lines in 60 files now uncovered.

314857 of 435233 relevant lines covered (72.34%)

1137439.04 hits per line

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

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

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

73
        if (context->tty_path)
12,001✔
74
                return context->tty_path;
492✔
75

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

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

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

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

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

95
        if (!tty_path)
522✔
96
                tty_path = exec_context_tty_path(context);
268✔
97

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

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

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

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

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

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

157
        if (context->tty_reset)
279✔
158
                (void) terminal_reset_defensive(
216✔
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));
213✔
162

163
        r = exec_context_apply_tty_size(context, fd, fd, path);
279✔
164
        if (r < 0)
279✔
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)) {
523✔
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)
279✔
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);
279✔
190

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

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

198
        return context->private_network || context->network_namespace_path;
62,277✔
199
}
200

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

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

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

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

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

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

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

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

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

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

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

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

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

259
bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params) {
33,245✔
260
        assert(context);
33,245✔
261

262
        if (context->root_image ||
33,245✔
263
            context->root_mstack)
33,159✔
264
                return true;
265

266
        if (context->root_directory_as_fd)
33,139✔
267
                return true;
268

269
        if (!strv_isempty(context->read_write_paths) ||
33,133✔
270
            !strv_isempty(context->read_only_paths) ||
30,858✔
271
            !strv_isempty(context->inaccessible_paths) ||
30,851✔
272
            !strv_isempty(context->exec_paths) ||
30,826✔
273
            !strv_isempty(context->no_exec_paths))
30,826✔
274
                return true;
275

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

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

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

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

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

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

294
        if (context->private_devices ||
30,556✔
295
            context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */
29,122✔
296
            context->private_mounts > 0 ||
29,024✔
297
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,544✔
298
            context->protect_system != PROTECT_SYSTEM_NO ||
28,509✔
299
            context->protect_home != PROTECT_HOME_NO ||
28,509✔
300
            context->protect_kernel_tunables ||
28,509✔
301
            context->protect_kernel_modules ||
28,509✔
302
            context->protect_kernel_logs ||
53,621✔
303
            exec_needs_cgroup_mount(context) ||
26,809✔
304
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,791✔
305
            context->proc_subset != PROC_SUBSET_ALL ||
26,717✔
306
            context->private_bpf != PRIVATE_BPF_NO ||
53,416✔
307
            exec_needs_ipc_namespace(context) ||
53,416✔
308
            exec_needs_pid_namespace(context, params))
26,708✔
309
                return true;
3,895✔
310

311
        if (context->root_directory) {
26,661✔
312
                if (exec_context_get_effective_mount_apivfs(context))
4✔
313
                        return true;
314

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

319
                        if (context->directories[t].n_items > 0)
×
320
                                return true;
321
                }
322
        }
323

324
        if (context->dynamic_user &&
26,657✔
325
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
326
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
327
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
328
                return true;
329

330
        if (exec_context_get_effective_bind_log_sockets(context))
26,657✔
331
                return true;
332

333
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
159,598✔
334
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
138,044✔
335
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,097✔
336
                                return true;
337

338
        return false;
339
}
340

341
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,928✔
342
        assert(context);
3,928✔
343
        assert(params);
3,928✔
344

345
        if (!params->notify_socket)
3,928✔
346
                return NULL;
347

348
        if (!needs_sandboxing)
3,221✔
349
                return NULL;
350

351
        if (!exec_context_with_rootfs(context))
3,221✔
352
                return NULL;
353

354
        if (!exec_context_get_effective_mount_apivfs(context))
×
355
                return NULL;
356

357
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
358
                return NULL;
×
359

360
        return "/run/host/notify";
361
}
362

363
int exec_log_level_max_with_exec_params(const ExecContext *context, const ExecParameters *params) {
20,306✔
364
        assert(params);
20,306✔
365

366
        if (params->debug_invocation)
20,306✔
367
                return LOG_DEBUG;
368

369
        return exec_log_level_max(context);
20,302✔
370
}
371

372
int exec_log_level_max(const ExecContext *context) {
20,354✔
373
        assert(context);
20,354✔
374
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
20,354✔
375
}
376

377
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
10,049✔
378
        assert(context);
10,049✔
379

380
        if (!context->dynamic_user)
10,049✔
381
                return false;
382

383
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
106✔
384
                return false;
385

386
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
100✔
387
                return false;
16✔
388

389
        return true;
390
}
391

392
int exec_params_needs_control_subcgroup(const ExecParameters *params) {
2,536✔
393
        /* Keep this in sync with exec_params_get_cgroup_path(). */
394
        return FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE|EXEC_CONTROL_CGROUP|EXEC_IS_CONTROL);
2,536✔
395
}
396

397
int exec_params_get_cgroup_path(
10,871✔
398
                const ExecParameters *params,
399
                const CGroupContext *c,
400
                const char *prefix,
401
                char **ret) {
402

403
        const char *subgroup = NULL;
10,871✔
404
        char *p;
10,871✔
405

406
        assert(params);
10,871✔
407
        assert(c);
10,871✔
408
        assert(ret);
10,871✔
409

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

419
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
420
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
10,871✔
421
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
716✔
422
                        subgroup = ".control";
423
                else
424
                        subgroup = c->delegate_subgroup;
670✔
425
        }
426

427
        if (subgroup)
670✔
428
                p = path_join(prefix, subgroup);
716✔
429
        else
430
                p = strdup(strempty(prefix));
10,163✔
431
        if (!p)
10,871✔
432
                return -ENOMEM;
433

434
        *ret = p;
10,871✔
435
        return !!subgroup;
10,871✔
436
}
437

438
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
3,048✔
439
        assert(c);
3,048✔
440

441
        return c->cpu_affinity_from_numa;
3,048✔
442
}
443

444
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,506✔
445
        assert(unit);
2,506✔
446
        assert(msg);
2,506✔
447
        assert(executable);
2,506✔
448

449
        if (!DEBUG_LOGGING)
2,506✔
450
                return;
2,506✔
451

452
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
5,012✔
453

454
        log_unit_struct(unit, LOG_DEBUG,
2,506✔
455
                        LOG_ITEM("EXECUTABLE=%s", executable),
456
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
457
                        LOG_UNIT_INVOCATION_ID(unit));
458
}
459

460
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret);
461

462
int exec_spawn(
2,506✔
463
                Unit *unit,
464
                ExecCommand *command,
465
                const ExecContext *context,
466
                ExecParameters *params,
467
                ExecRuntime *runtime,
468
                const CGroupContext *cgroup_context,
469
                PidRef *ret) {
470

471
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,506✔
472
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
473
        _cleanup_fclose_ FILE *f = NULL;
2,506✔
474
        int r;
2,506✔
475

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

488
        LOG_CONTEXT_PUSH_UNIT(unit);
5,012✔
489

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

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

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

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

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

519
                cgtarget = subcgroup_path;
4✔
520
        } else
521
                cgtarget = params->cgroup_path;
2,502✔
522

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

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

534
        fdset = fdset_new();
2,506✔
535
        if (!fdset)
2,506✔
536
                return log_oom();
×
537

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

542
        r = finish_serialization_file(f);
2,506✔
543
        if (r < 0)
2,506✔
544
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
545

546
        r = fd_cloexec(fileno(f), false);
2,506✔
547
        if (r < 0)
2,506✔
548
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
549

550
        r = fdset_cloexec(fdset, false);
2,506✔
551
        if (r < 0)
2,506✔
552
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
553

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

561
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,506✔
562
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,506✔
563

564
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,506✔
565
        dual_timestamp start_timestamp;
2,506✔
566

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

573
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
574
         * handoff timestamp. */
575
        dual_timestamp_now(&start_timestamp);
2,506✔
576

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

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

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

605
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,506✔
606
                       command->path, pidref.pid, r > 0 ? "via" : "without");
607

608
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,506✔
609

610
        *ret = TAKE_PIDREF(pidref);
2,506✔
611
        return 0;
2,506✔
612
}
613

614
void exec_context_init(ExecContext *c) {
67,807✔
615
        assert(c);
67,807✔
616

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

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

647
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
406,842✔
648
                d->mode = 0755;
339,035✔
649

650
        numa_policy_reset(&c->numa_policy);
67,807✔
651

652
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
67,807✔
653
}
67,807✔
654

655
void exec_context_done(ExecContext *c) {
57,689✔
656
        assert(c);
57,689✔
657

658
        c->environment = strv_free(c->environment);
57,689✔
659
        c->environment_files = strv_free(c->environment_files);
57,689✔
660
        c->pass_environment = strv_free(c->pass_environment);
57,689✔
661
        c->unset_environment = strv_free(c->unset_environment);
57,689✔
662

663
        rlimit_free_all(c->rlimit);
57,689✔
664

665
        for (size_t l = 0; l < 3; l++) {
230,756✔
666
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
173,067✔
667
                c->stdio_file[l] = mfree(c->stdio_file[l]);
173,067✔
668
        }
669

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

685
        c->supplementary_groups = strv_free(c->supplementary_groups);
57,689✔
686

687
        c->pam_name = mfree(c->pam_name);
57,689✔
688

689
        c->read_only_paths = strv_free(c->read_only_paths);
57,689✔
690
        c->read_write_paths = strv_free(c->read_write_paths);
57,689✔
691
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
57,689✔
692
        c->exec_paths = strv_free(c->exec_paths);
57,689✔
693
        c->no_exec_paths = strv_free(c->no_exec_paths);
57,689✔
694
        c->exec_search_path = strv_free(c->exec_search_path);
57,689✔
695

696
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
57,689✔
697
        c->bind_mounts = NULL;
57,689✔
698
        c->n_bind_mounts = 0;
57,689✔
699
        mount_image_free_many(c->mount_images, c->n_mount_images);
57,689✔
700
        c->mount_images = NULL;
57,689✔
701
        c->n_mount_images = 0;
57,689✔
702
        mount_image_free_many(c->extension_images, c->n_extension_images);
57,689✔
703
        c->extension_images = NULL;
57,689✔
704
        c->n_extension_images = 0;
57,689✔
705
        c->extension_directories = strv_free(c->extension_directories);
57,689✔
706
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
57,689✔
707
        c->temporary_filesystems = NULL;
57,689✔
708
        c->n_temporary_filesystems = 0;
57,689✔
709

710
        cpu_set_done(&c->cpu_set);
57,689✔
711
        numa_policy_reset(&c->numa_policy);
57,689✔
712

713
        c->utmp_id = mfree(c->utmp_id);
57,689✔
714
        c->selinux_context = mfree(c->selinux_context);
57,689✔
715
        c->apparmor_profile = mfree(c->apparmor_profile);
57,689✔
716
        c->smack_process_label = mfree(c->smack_process_label);
57,689✔
717

718
        c->restrict_filesystems = set_free(c->restrict_filesystems);
57,689✔
719

720
        c->syscall_filter = hashmap_free(c->syscall_filter);
57,689✔
721
        c->syscall_archs = set_free(c->syscall_archs);
57,689✔
722
        c->syscall_log = hashmap_free(c->syscall_log);
57,689✔
723
        c->address_families = set_free(c->address_families);
57,689✔
724

725
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
346,134✔
726
                exec_directory_done(d);
288,445✔
727

728
        c->log_level_max = -1;
57,689✔
729

730
        exec_context_free_log_extra_fields(c);
57,689✔
731
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
57,689✔
732
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
57,689✔
733

734
        c->log_ratelimit = (RateLimit) {};
57,689✔
735

736
        c->stdin_data = mfree(c->stdin_data);
57,689✔
737
        c->stdin_data_size = 0;
57,689✔
738

739
        c->user_namespace_path = mfree(c->user_namespace_path);
57,689✔
740
        c->network_namespace_path = mfree(c->network_namespace_path);
57,689✔
741
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
57,689✔
742

743
        c->log_namespace = mfree(c->log_namespace);
57,689✔
744

745
        c->load_credentials = hashmap_free(c->load_credentials);
57,689✔
746
        c->set_credentials = hashmap_free(c->set_credentials);
57,689✔
747
        c->import_credentials = ordered_set_free(c->import_credentials);
57,689✔
748

749
        c->root_image_policy = image_policy_free(c->root_image_policy);
57,689✔
750
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
57,689✔
751
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
57,689✔
752

753
        c->private_hostname = mfree(c->private_hostname);
57,689✔
754
}
57,689✔
755

756
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
6,596✔
757
        assert(c);
6,596✔
758

759
        if (!runtime_prefix)
6,596✔
760
                return 0;
761

762
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
6,611✔
763
                _cleanup_free_ char *p = NULL;
15✔
764

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

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

776
                STRV_FOREACH(symlink, i->symlinks) {
15✔
777
                        _cleanup_free_ char *symlink_abs = NULL;
×
778

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

786
                        (void) unlink(symlink_abs);
×
787
                }
788
        }
789

790
        return 0;
791
}
792

793
int exec_context_destroy_mount_ns_dir(Unit *u) {
13,950✔
794
        _cleanup_free_ char *p = NULL;
13,950✔
795

796
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
13,950✔
797
                return 0;
798

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

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

807
        return 0;
808
}
809

810
void exec_command_done(ExecCommand *c) {
121,797✔
811
        assert(c);
121,797✔
812

813
        c->path = mfree(c->path);
121,797✔
814
        c->argv = strv_free(c->argv);
121,797✔
815
}
121,797✔
816

817
void exec_command_done_array(ExecCommand *c, size_t n) {
34,371✔
818
        FOREACH_ARRAY(i, c, n)
137,482✔
819
                exec_command_done(i);
103,111✔
820
}
34,371✔
821

822
ExecCommand* exec_command_free(ExecCommand *c) {
18,652✔
823
        if (!c)
18,652✔
824
                return NULL;
825

826
        exec_command_done(c);
18,652✔
827
        return mfree(c);
18,652✔
828
}
829

830
ExecCommand* exec_command_free_list(ExecCommand *c) {
163,477✔
831
        ExecCommand *i;
163,477✔
832

833
        while ((i = LIST_POP(command, c)))
182,129✔
834
                exec_command_free(i);
18,652✔
835

836
        return NULL;
163,477✔
837
}
838

839
void exec_command_free_array(ExecCommand **c, size_t n) {
23,284✔
840
        FOREACH_ARRAY(i, c, n)
186,741✔
841
                *i = exec_command_free_list(*i);
163,457✔
842
}
23,284✔
843

844
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
8,510✔
845
        FOREACH_ARRAY(i, c, n)
34,039✔
846
                exec_status_reset(&i->exec_status);
25,529✔
847
}
8,510✔
848

849
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,733✔
850
        FOREACH_ARRAY(i, c, n)
40,887✔
851
                LIST_FOREACH(command, z, *i)
37,588✔
852
                        exec_status_reset(&z->exec_status);
2,434✔
853
}
5,733✔
854

855
typedef struct InvalidEnvInfo {
856
        const Unit *unit;
857
        const char *path;
858
} InvalidEnvInfo;
859

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

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

866
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,603✔
867
        assert(c);
39,603✔
868

869
        switch (fd_index) {
39,603✔
870

871
        case STDIN_FILENO:
13,201✔
872
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,201✔
873
                        return NULL;
874

875
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
876

877
        case STDOUT_FILENO:
13,201✔
878
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,201✔
879
                        return NULL;
880

881
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
882

883
        case STDERR_FILENO:
13,201✔
884
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,201✔
885
                        return NULL;
886

887
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
888

889
        default:
890
                return NULL;
891
        }
892
}
893

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

898
        assert(c);
2,506✔
899
        assert(ret);
2,506✔
900

901
        STRV_FOREACH(i, c->environment_files) {
2,509✔
902
                _cleanup_strv_free_ char **paths = NULL;
3✔
903
                bool ignore = false;
3✔
904
                char *fn = *i;
3✔
905

906
                if (fn[0] == '-') {
3✔
907
                        ignore = true;
2✔
908
                        fn++;
2✔
909
                }
910

911
                if (!path_is_absolute(fn)) {
3✔
912
                        if (ignore)
×
913
                                continue;
×
914
                        return -EINVAL;
915
                }
916

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

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

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

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

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

945
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
946
                        }
947

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

955
                                strv_free_and_replace(v, m);
×
956
                        }
957
                }
958
        }
959

960
        *ret = TAKE_PTR(v);
2,506✔
961

962
        return 0;
2,506✔
963
}
964

965
static bool tty_may_match_dev_console(const char *tty) {
250✔
966
        _cleanup_free_ char *resolved = NULL;
250✔
967

968
        if (!tty)
250✔
969
                return true;
970

971
        tty = skip_dev_prefix(tty);
250✔
972

973
        /* trivial identity? */
974
        if (streq(tty, "console"))
250✔
975
                return true;
976

977
        if (resolve_dev_console(&resolved) < 0)
38✔
978
                return true; /* if we could not resolve, assume it may */
979

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

984
static bool exec_context_may_touch_tty(const ExecContext *ec) {
28,778✔
985
        assert(ec);
28,778✔
986

987
        return ec->tty_reset ||
57,320✔
988
                ec->tty_vhangup ||
28,542✔
989
                ec->tty_vt_disallocate ||
28,542✔
990
                exec_input_is_terminal(ec->std_input) ||
28,542✔
991
                ec->std_output == EXEC_OUTPUT_TTY ||
57,320✔
992
                ec->std_error == EXEC_OUTPUT_TTY;
28,493✔
993
}
994

995
bool exec_context_may_touch_console(const ExecContext *ec) {
27,065✔
996

997
        return exec_context_may_touch_tty(ec) &&
27,315✔
998
               tty_may_match_dev_console(exec_context_tty_path(ec));
250✔
999
}
1000

1001
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,194✔
1002
        assert(c);
1,194✔
1003

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

1011
        if (!c->tty_reset)
1,194✔
1012
                return false;
1013

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

1020
static void strv_fprintf(FILE *f, char **l) {
2✔
1021
        assert(f);
2✔
1022

1023
        STRV_FOREACH(g, l)
6✔
1024
                fprintf(f, " %s", *g);
4✔
1025
}
2✔
1026

1027
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
3,392✔
1028
        assert(f);
3,392✔
1029
        assert(prefix);
3,392✔
1030
        assert(name);
3,392✔
1031

1032
        if (!strv_isempty(strv)) {
3,392✔
1033
                fprintf(f, "%s%s:", prefix, name);
2✔
1034
                strv_fprintf(f, strv);
2✔
1035
                fputs("\n", f);
2✔
1036
        }
1037
}
3,392✔
1038

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

1043
        prefix = strempty(prefix);
×
1044

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

1071
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1072
        strv_dump(f, prefix, "Environment", p->environment);
×
1073
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1074

1075
        LIST_FOREACH(open_files, file, p->open_files)
×
1076
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1077

1078
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1079
}
×
1080

1081
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
424✔
1082
        int r;
424✔
1083

1084
        assert(c);
424✔
1085
        assert(f);
424✔
1086

1087
        prefix = strempty(prefix);
424✔
1088

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

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

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

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

1165
        if (c->root_image)
424✔
1166
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1167

1168
        if (c->root_image_options) {
424✔
1169
                _cleanup_free_ char *opts_str = NULL;
×
1170

1171
                if (mount_options_to_string(c->root_image_options, &opts_str) >= 0 && !isempty(opts_str))
×
1172
                        fprintf(f, "%sRootImageOptions: %s\n", prefix, opts_str);
×
1173
        }
1174

1175
        if (iovec_is_set(&c->root_hash)) {
424✔
1176
                _cleanup_free_ char *encoded = NULL;
×
1177
                encoded = hexmem(c->root_hash.iov_base, c->root_hash.iov_len);
×
1178
                if (encoded)
×
1179
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1180
        }
1181

1182
        if (c->root_hash_path)
424✔
1183
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1184

1185
        if (iovec_is_set(&c->root_hash_sig)) {
1186
                _cleanup_free_ char *encoded = NULL;
×
1187
                ssize_t len;
×
1188
                len = base64mem(c->root_hash_sig.iov_base, c->root_hash_sig.iov_len, &encoded);
×
1189
                if (len)
×
1190
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1191
        }
1192

1193
        if (c->root_hash_sig_path)
424✔
1194
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1195

1196
        if (c->root_verity)
424✔
1197
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1198

1199
        if (c->root_mstack)
424✔
1200
                fprintf(f, "%sRootMStack: %s\n", prefix, c->root_mstack);
×
1201

1202
        STRV_FOREACH(e, c->environment)
431✔
1203
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
7✔
1204

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

1208
        STRV_FOREACH(e, c->pass_environment)
436✔
1209
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
12✔
1210

1211
        STRV_FOREACH(e, c->unset_environment)
439✔
1212
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
15✔
1213

1214
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
424✔
1215

1216
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
2,544✔
1217
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
2,120✔
1218

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

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

1232
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
424✔
1233

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

1237
        if (c->nice_set)
424✔
1238
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
2✔
1239

1240
        if (c->oom_score_adjust_set)
424✔
1241
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
16✔
1242

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

1246
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
7,208✔
1247
                if (c->rlimit[i]) {
6,784✔
1248
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
364✔
1249
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1250
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
364✔
1251
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
364✔
1252
                }
1253

1254
        if (c->ioprio_is_set) {
424✔
1255
                _cleanup_free_ char *class_str = NULL;
3✔
1256

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

1261
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
3✔
1262
        }
1263

1264
        if (c->cpu_sched_set) {
424✔
1265
                _cleanup_free_ char *policy_str = NULL;
×
1266

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

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

1278
        if (c->cpu_set.set) {
424✔
1279
                _cleanup_free_ char *affinity = NULL;
×
1280

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

1285
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
424✔
1286
                _cleanup_free_ char *nodes = NULL;
1✔
1287

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

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

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

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

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

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

1341
        if (IN_SET(c->std_output,
424✔
1342
                   EXEC_OUTPUT_KMSG,
1343
                   EXEC_OUTPUT_JOURNAL,
1344
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1345
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1346
            IN_SET(c->std_error,
24✔
1347
                   EXEC_OUTPUT_KMSG,
1348
                   EXEC_OUTPUT_JOURNAL,
1349
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1350
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1351

1352
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
400✔
1353

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

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

1363
        if (c->log_level_max >= 0) {
424✔
1364
                _cleanup_free_ char *t = NULL;
1✔
1365

1366
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1367

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

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

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

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

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

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

1396
        if (c->log_namespace)
424✔
1397
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1398

1399
        if (c->secure_bits) {
424✔
1400
                _cleanup_free_ char *str = NULL;
×
1401

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

1407
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
424✔
1408
                _cleanup_free_ char *str = NULL;
424✔
1409

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

1415
        if (c->capability_ambient_set != 0) {
424✔
1416
                _cleanup_free_ char *str = NULL;
3✔
1417

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

1423
        if (c->user)
424✔
1424
                fprintf(f, "%sUser: %s\n", prefix, c->user);
7✔
1425
        if (c->group)
424✔
1426
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1427

1428
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
847✔
1429

1430
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
424✔
1431

1432
        if (c->pam_name)
424✔
1433
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
1✔
1434

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

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

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

1456
        if (c->utmp_id)
424✔
1457
                fprintf(f,
2✔
1458
                        "%sUtmpIdentifier: %s\n",
1459
                        prefix, c->utmp_id);
1460

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

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

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

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

1481
        fprintf(f,
424✔
1482
                "%sLockPersonality: %s\n",
1483
                prefix, yes_no(c->lock_personality));
424✔
1484

1485
        if (c->syscall_filter) {
424✔
1486
                fprintf(f,
24✔
1487
                        "%sSystemCallFilter: ",
1488
                        prefix);
1489

1490
                if (!c->syscall_allow_list)
24✔
1491
                        fputc('~', f);
×
1492

1493
#if HAVE_SECCOMP
1494
                if (dlopen_libseccomp() >= 0) {
24✔
1495
                        void *id, *val;
24✔
1496
                        bool first = true;
24✔
1497
                        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
9,404✔
1498
                                _cleanup_free_ char *name = NULL;
9,380✔
1499
                                const char *errno_name = NULL;
9,380✔
1500
                                int num = PTR_TO_INT(val);
9,380✔
1501

1502
                                if (first)
9,380✔
1503
                                        first = false;
1504
                                else
1505
                                        fputc(' ', f);
9,356✔
1506

1507
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
9,380✔
1508
                                fputs(strna(name), f);
9,380✔
1509

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

1521
                fputc('\n', f);
24✔
1522
        }
1523

1524
        if (c->syscall_archs) {
424✔
1525
                fprintf(f,
26✔
1526
                        "%sSystemCallArchitectures:",
1527
                        prefix);
1528

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

1537
        if (exec_context_restrict_namespaces_set(c)) {
424✔
1538
                _cleanup_free_ char *s = NULL;
22✔
1539

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

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

1554
        if (c->user_namespace_path)
424✔
1555
                fprintf(f,
×
1556
                        "%sUserNamespacePath: %s\n",
1557
                        prefix, c->user_namespace_path);
1558

1559
        if (c->network_namespace_path)
424✔
1560
                fprintf(f,
×
1561
                        "%sNetworkNamespacePath: %s\n",
1562
                        prefix, c->network_namespace_path);
1563

1564
        if (c->syscall_errno > 0) {
424✔
1565
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
423✔
1566

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

1577
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
424✔
1578
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1579
                        mount->ignore_enoent ? "-": "",
×
1580
                        mount->source,
1581
                        mount->destination);
1582
                if (mount->mount_options) {
×
1583
                        _cleanup_free_ char *opts = NULL;
×
1584

1585
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1586
                                fprintf(f, " %s", opts);
×
1587
                }
1588
                fprintf(f, "\n");
×
1589
        }
1590

1591
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
424✔
1592
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1593
                        mount->ignore_enoent ? "-": "",
×
1594
                        mount->source);
1595
                if (mount->mount_options) {
×
1596
                        _cleanup_free_ char *opts = NULL;
×
1597

1598
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1599
                                fprintf(f, " %s", opts);
×
1600
                }
1601
                fprintf(f, "\n");
×
1602
        }
1603

1604
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
424✔
1605
}
424✔
1606

1607
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1608
        assert(c);
×
1609

1610
        /* Returns true if the process forked off would run under
1611
         * an unchanged UID or as root. */
1612

1613
        if (!c->user)
×
1614
                return true;
1615

1616
        if (STR_IN_SET(c->user, "root", "0"))
×
1617
                return true;
×
1618

1619
        return false;
×
1620
}
1621

1622
int exec_context_get_effective_ioprio(const ExecContext *c) {
6,096✔
1623
        int p;
6,096✔
1624

1625
        assert(c);
6,096✔
1626

1627
        if (c->ioprio_is_set)
6,096✔
1628
                return c->ioprio;
14✔
1629

1630
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
6,082✔
1631
        if (p < 0)
6,082✔
1632
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1633

1634
        return ioprio_normalize(p);
6,082✔
1635
}
1636

1637
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
39,691✔
1638
        assert(c);
39,691✔
1639

1640
        /* Explicit setting wins */
1641
        if (c->mount_apivfs >= 0)
39,691✔
1642
                return c->mount_apivfs > 0;
119✔
1643

1644
        /* Default to "yes" if root directory or image are specified */
1645
        if (exec_context_with_rootfs(c))
39,572✔
1646
                return true;
265✔
1647

1648
        return false;
1649
}
1650

1651
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
32,161✔
1652
        assert(c);
32,161✔
1653

1654
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1655
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1656
        if (c->log_namespace)
32,161✔
1657
                return true;
1658

1659
        if (c->bind_log_sockets >= 0)
32,153✔
1660
                return c->bind_log_sockets > 0;
2✔
1661

1662
        if (exec_context_get_effective_mount_apivfs(c))
32,151✔
1663
                return true;
1664

1665
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1666
        if (exec_context_with_rootfs(c) && c->private_devices)
31,983✔
1667
                return true;
×
1668

1669
        return false;
1670
}
1671

1672
void exec_context_free_log_extra_fields(ExecContext *c) {
57,691✔
1673
        assert(c);
57,691✔
1674

1675
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
58,206✔
1676
                free(field->iov_base);
515✔
1677

1678
        c->log_extra_fields = mfree(c->log_extra_fields);
57,691✔
1679
        c->n_log_extra_fields = 0;
57,691✔
1680
}
57,691✔
1681

1682
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,713✔
1683
        _cleanup_close_ int fd = -EBADF;
1,713✔
1684
        const char *path;
1,713✔
1685
        struct stat st;
1,713✔
1686
        int r;
1,713✔
1687

1688
        assert(c);
1,713✔
1689

1690
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1691
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,713✔
1692

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

1699
        path = exec_context_tty_path(c);
35✔
1700
        if (!path)
35✔
1701
                return;
1702

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

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

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

1720
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
35✔
1721
        if (r < 0)
35✔
1722
                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✔
1723
}
1724

1725
int exec_context_get_clean_directories(
1✔
1726
                ExecContext *c,
1727
                char **prefix,
1728
                ExecCleanMask mask,
1729
                char ***ret) {
1730

1731
        _cleanup_strv_free_ char **l = NULL;
1✔
1732
        int r;
1✔
1733

1734
        assert(c);
1✔
1735
        assert(prefix);
1✔
1736
        assert(ret);
1✔
1737

1738
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
6✔
1739
                if (!BIT_SET(mask, t))
5✔
1740
                        continue;
×
1741

1742
                if (!prefix[t])
5✔
1743
                        continue;
×
1744

1745
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
7✔
1746
                        char *j;
2✔
1747

1748
                        j = path_join(prefix[t], i->path);
2✔
1749
                        if (!j)
2✔
1750
                                return -ENOMEM;
1751

1752
                        r = strv_consume(&l, j);
2✔
1753
                        if (r < 0)
2✔
1754
                                return r;
1755

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

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

1767
                        STRV_FOREACH(symlink, i->symlinks) {
2✔
1768
                                j = path_join(prefix[t], *symlink);
×
1769
                                if (!j)
×
1770
                                        return -ENOMEM;
1771

1772
                                r = strv_consume(&l, j);
×
1773
                                if (r < 0)
×
1774
                                        return r;
1775
                        }
1776
                }
1777
        }
1778

1779
        *ret = TAKE_PTR(l);
1✔
1780
        return 0;
1✔
1781
}
1782

1783
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
3,032✔
1784
        ExecCleanMask mask = 0;
3,032✔
1785

1786
        assert(c);
3,032✔
1787
        assert(ret);
3,032✔
1788

1789
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
18,192✔
1790
                if (c->directories[t].n_items > 0)
15,160✔
1791
                        mask |= 1U << t;
315✔
1792

1793
        *ret = mask;
3,032✔
1794
        return 0;
3,032✔
1795
}
1796

1797
int exec_context_get_oom_score_adjust(const ExecContext *c) {
3,048✔
1798
        int n = 0, r;
3,048✔
1799

1800
        assert(c);
3,048✔
1801

1802
        if (c->oom_score_adjust_set)
3,048✔
1803
                return c->oom_score_adjust;
719✔
1804

1805
        r = get_oom_score_adjust(&n);
2,329✔
1806
        if (r < 0)
2,329✔
1807
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1808

1809
        return n;
2,329✔
1810
}
1811

1812
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
3,048✔
1813
        _cleanup_free_ char *t = NULL;
3,048✔
1814
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
3,048✔
1815
        int r;
3,048✔
1816

1817
        assert(c);
3,048✔
1818

1819
        if (c->coredump_filter_set)
3,048✔
1820
                return c->coredump_filter;
×
1821

1822
        r = read_one_line_file("/proc/self/coredump_filter", &t);
3,048✔
1823
        if (r < 0)
3,048✔
1824
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1825
        else {
1826
                r = safe_atoux64(t, &n);
3,048✔
1827
                if (r < 0)
3,048✔
1828
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1829
        }
1830

1831
        return n;
3,048✔
1832
}
1833

1834
int exec_context_get_nice(const ExecContext *c) {
3,048✔
1835
        int n;
3,048✔
1836

1837
        assert(c);
3,048✔
1838

1839
        if (c->nice_set)
3,048✔
1840
                return c->nice;
14✔
1841

1842
        errno = 0;
3,034✔
1843
        n = getpriority(PRIO_PROCESS, 0);
3,034✔
1844
        if (errno > 0) {
3,034✔
1845
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1846
                n = 0;
1847
        }
1848

1849
        return n;
1850
}
1851

1852
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
3,048✔
1853
        int n;
3,048✔
1854

1855
        assert(c);
3,048✔
1856

1857
        if (c->cpu_sched_set)
3,048✔
1858
                return c->cpu_sched_policy;
×
1859

1860
        n = sched_getscheduler(0);
3,048✔
1861
        if (n < 0)
3,048✔
1862
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1863

1864
        return n < 0 ? SCHED_OTHER : n;
3,048✔
1865
}
1866

1867
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
3,048✔
1868
        struct sched_param p = {};
3,048✔
1869
        int r;
3,048✔
1870

1871
        assert(c);
3,048✔
1872

1873
        if (c->cpu_sched_set)
3,048✔
1874
                return c->cpu_sched_priority;
×
1875

1876
        r = sched_getparam(0, &p);
3,048✔
1877
        if (r < 0)
3,048✔
1878
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1879

1880
        return r >= 0 ? p.sched_priority : 0;
3,048✔
1881
}
1882

1883
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
3,048✔
1884
        int r;
3,048✔
1885

1886
        assert(c);
3,048✔
1887

1888
        if (c->timer_slack_nsec != NSEC_INFINITY)
3,048✔
1889
                return c->timer_slack_nsec;
1890

1891
        r = prctl(PR_GET_TIMERSLACK);
3,048✔
1892
        if (r < 0)
3,048✔
1893
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1894

1895
        return (uint64_t) MAX(r, 0);
3,048✔
1896
}
1897

1898
bool exec_context_get_set_login_environment(const ExecContext *c) {
13,143✔
1899
        assert(c);
13,143✔
1900

1901
        if (c->set_login_environment >= 0)
13,143✔
1902
                return c->set_login_environment;
×
1903

1904
        return c->user || c->dynamic_user || c->pam_name;
22,791✔
1905
}
1906

1907
char** exec_context_get_syscall_filter(const ExecContext *c) {
3,048✔
1908
        _cleanup_strv_free_ char **l = NULL;
3,048✔
1909

1910
        assert(c);
3,048✔
1911

1912
#if HAVE_SECCOMP
1913
        if (dlopen_libseccomp() < 0)
3,048✔
1914
                return strv_new(NULL);
×
1915

1916
        void *id, *val;
3,048✔
1917
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
14,470✔
1918
                _cleanup_free_ char *name = NULL;
11,422✔
1919
                const char *e = NULL;
11,422✔
1920
                char *s;
11,422✔
1921
                int num = PTR_TO_INT(val);
11,422✔
1922

1923
                if (c->syscall_allow_list && num >= 0)
11,422✔
1924
                        /* syscall with num >= 0 in allow-list is denied. */
1925
                        continue;
×
1926

1927
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
11,422✔
1928
                if (!name)
11,422✔
1929
                        continue;
×
1930

1931
                if (num >= 0) {
11,422✔
1932
                        e = seccomp_errno_or_action_to_string(num);
×
1933
                        if (e) {
×
1934
                                s = strjoin(name, ":", e);
×
1935
                                if (!s)
×
1936
                                        return NULL;
1937
                        } else {
1938
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1939
                                        return NULL;
1940
                        }
1941
                } else
1942
                        s = TAKE_PTR(name);
11,422✔
1943

1944
                if (strv_consume(&l, s) < 0)
11,422✔
1945
                        return NULL;
1946
        }
1947

1948
        strv_sort(l);
3,048✔
1949
#endif
1950

1951
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,048✔
1952
}
1953

1954
char** exec_context_get_syscall_archs(const ExecContext *c) {
3,048✔
1955
        _cleanup_strv_free_ char **l = NULL;
3,048✔
1956

1957
        assert(c);
3,048✔
1958

1959
#if HAVE_SECCOMP
1960
        void *id;
3,048✔
1961
        SET_FOREACH(id, c->syscall_archs) {
3,081✔
1962
                const char *name;
33✔
1963

1964
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
33✔
1965
                if (!name)
33✔
1966
                        continue;
×
1967

1968
                if (strv_extend(&l, name) < 0)
33✔
1969
                        return NULL;
×
1970
        }
1971

1972
        strv_sort(l);
3,048✔
1973
#endif
1974

1975
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,048✔
1976
}
1977

1978
char** exec_context_get_syscall_log(const ExecContext *c) {
3,048✔
1979
        _cleanup_strv_free_ char **l = NULL;
3,048✔
1980

1981
        assert(c);
3,048✔
1982

1983
#if HAVE_SECCOMP
1984
        if (dlopen_libseccomp() < 0)
3,048✔
1985
                return strv_new(NULL);
×
1986

1987
        void *id, *val;
3,048✔
1988
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
3,048✔
1989
                char *name = NULL;
×
1990

1991
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1992
                if (!name)
×
1993
                        continue;
×
1994

1995
                if (strv_consume(&l, name) < 0)
×
1996
                        return NULL;
×
1997
        }
1998

1999
        strv_sort(l);
3,048✔
2000
#endif
2001

2002
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,048✔
2003
}
2004

2005
char** exec_context_get_address_families(const ExecContext *c) {
3,048✔
2006
        _cleanup_strv_free_ char **l = NULL;
3,048✔
2007
        void *af;
3,048✔
2008

2009
        assert(c);
3,048✔
2010

2011
        SET_FOREACH(af, c->address_families) {
3,154✔
2012
                const char *name;
106✔
2013

2014
                name = af_to_name(PTR_TO_INT(af));
106✔
2015
                if (!name)
106✔
2016
                        continue;
×
2017

2018
                if (strv_extend(&l, name) < 0)
106✔
2019
                        return NULL;
×
2020
        }
2021

2022
        strv_sort(l);
3,048✔
2023

2024
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,048✔
2025
}
2026

2027
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
3,048✔
2028
        assert(c);
3,048✔
2029

2030
#if HAVE_LIBBPF
2031
        char **l = set_get_strv(c->restrict_filesystems);
3,048✔
2032
        if (!l)
3,048✔
2033
                return NULL;
2034

2035
        return strv_sort(l);
3,048✔
2036
#else
2037
        return strv_new(NULL);
2038
#endif
2039
}
2040

2041
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,771✔
2042
        assert(c);
13,771✔
2043

2044
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,771✔
2045
}
2046

2047
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
15,546✔
2048
        assert(c);
15,546✔
2049

2050
        return c->restrict_filesystems_allow_list ||
15,546✔
2051
          !set_isempty(c->restrict_filesystems);
15,546✔
2052
}
2053

2054
bool exec_context_with_rootfs(const ExecContext *c) {
75,217✔
2055
        assert(c);
75,217✔
2056

2057
        /* Checks if RootDirectory=, RootImage=, RootMStack= or RootDirectoryFileDescriptor= are used */
2058

2059
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd || c->root_mstack;
75,217✔
2060
}
2061

2062
bool exec_context_with_rootfs_strict(const ExecContext *c) {
1,425✔
2063
        assert(c);
1,425✔
2064

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

2069
        return c->root_directory || c->root_image || c->root_directory_as_fd || c->root_mstack;
1,425✔
2070
}
2071

2072
int exec_context_has_vpicked_extensions(const ExecContext *context) {
15,035✔
2073
        int r;
15,035✔
2074

2075
        assert(context);
15,035✔
2076

2077
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
15,091✔
2078
                r = path_uses_vpick(mi->source);
56✔
2079
                if (r != 0)
56✔
2080
                        return r;
2081
        }
2082
        STRV_FOREACH(ed, context->extension_directories) {
15,036✔
2083
                r = path_uses_vpick(*ed);
1✔
2084
                if (r != 0)
1✔
2085
                        return r;
2086
        }
2087

2088
        return 0;
2089
}
2090

2091
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
5,136✔
2092
        assert(s);
5,136✔
2093

2094
        *s = (ExecStatus) {
5,136✔
2095
                .pid = pid,
2096
        };
2097

2098
        if (ts)
5,136✔
2099
                s->start_timestamp = *ts;
5,136✔
2100
        else
2101
                dual_timestamp_now(&s->start_timestamp);
×
2102
}
5,136✔
2103

2104
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,321✔
2105
        assert(s);
2,321✔
2106

2107
        if (s->pid != pid)
2,321✔
2108
                *s = (ExecStatus) {
8✔
2109
                        .pid = pid,
2110
                };
2111

2112
        dual_timestamp_now(&s->exit_timestamp);
2,321✔
2113

2114
        s->code = code;
2,321✔
2115
        s->status = status;
2,321✔
2116

2117
        if (context && context->utmp_id)
2,321✔
2118
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2119
}
2,321✔
2120

2121
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
8,580✔
2122
        assert(s);
8,580✔
2123
        assert(ucred);
8,580✔
2124
        assert(ts);
8,580✔
2125

2126
        if (ucred->pid != s->pid)
8,580✔
2127
                *s = (ExecStatus) {
7✔
2128
                        .pid = ucred->pid,
2129
                };
2130

2131
        s->handoff_timestamp = *ts;
8,580✔
2132
}
8,580✔
2133

2134
void exec_status_reset(ExecStatus *s) {
30,126✔
2135
        assert(s);
30,126✔
2136

2137
        *s = (ExecStatus) {};
30,126✔
2138
}
30,126✔
2139

2140
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
225✔
2141
        assert(s);
225✔
2142
        assert(f);
225✔
2143

2144
        if (s->pid <= 0)
225✔
2145
                return;
2146

2147
        prefix = strempty(prefix);
42✔
2148

2149
        fprintf(f,
42✔
2150
                "%sPID: "PID_FMT"\n",
2151
                prefix, s->pid);
2152

2153
        if (dual_timestamp_is_set(&s->start_timestamp))
42✔
2154
                fprintf(f,
42✔
2155
                        "%sStart Timestamp: %s\n",
2156
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
42✔
2157

2158
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
42✔
2159
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
41✔
2160
                fprintf(f,
41✔
2161
                        "%sHandoff Timestamp: %s since start\n",
2162
                        prefix,
2163
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
82✔
2164
        else
2165
                fprintf(f,
1✔
2166
                        "%sHandoff Timestamp: %s\n",
2167
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
1✔
2168

2169
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
42✔
2170

2171
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
20✔
2172
                        fprintf(f,
20✔
2173
                                "%sExit Timestamp: %s since handoff\n",
2174
                                prefix,
2175
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
40✔
2176
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2177
                        fprintf(f,
×
2178
                                "%sExit Timestamp: %s since start\n",
2179
                                prefix,
2180
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2181
                else
2182
                        fprintf(f,
×
2183
                                "%sExit Timestamp: %s\n",
2184
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2185

2186
                fprintf(f,
20✔
2187
                        "%sExit Code: %s\n"
2188
                        "%sExit Status: %i\n",
2189
                        prefix, sigchld_code_to_string(s->code),
20✔
2190
                        prefix, s->status);
20✔
2191
        }
2192
}
2193

2194
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
225✔
2195
        _cleanup_free_ char *cmd = NULL;
450✔
2196
        const char *prefix2;
225✔
2197

2198
        assert(c);
225✔
2199
        assert(f);
225✔
2200

2201
        prefix = strempty(prefix);
225✔
2202
        prefix2 = strjoina(prefix, "\t");
1,125✔
2203

2204
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
225✔
2205

2206
        fprintf(f,
225✔
2207
                "%sCommand Line: %s\n",
2208
                prefix, strnull(cmd));
2209

2210
        exec_status_dump(&c->exec_status, f, prefix2);
225✔
2211
}
225✔
2212

2213
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
220✔
2214
        assert(f);
220✔
2215

2216
        prefix = strempty(prefix);
220✔
2217

2218
        LIST_FOREACH(command, i, c)
444✔
2219
                exec_command_dump(i, f, prefix);
224✔
2220
}
220✔
2221

2222
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
18,652✔
2223
        ExecCommand *end;
18,652✔
2224

2225
        assert(l);
18,652✔
2226
        assert(e);
18,652✔
2227

2228
        if (*l) {
18,652✔
2229
                /* It's kind of important, that we keep the order here */
2230
                end = LIST_FIND_TAIL(command, *l);
372✔
2231
                LIST_INSERT_AFTER(command, *l, end, e);
135✔
2232
        } else
2233
                *l = e;
18,517✔
2234
}
18,652✔
2235

2236
int exec_command_set(ExecCommand *c, const char *path, ...) {
203✔
2237
        va_list ap;
203✔
2238
        char **l, *p;
203✔
2239

2240
        assert(c);
203✔
2241
        assert(path);
203✔
2242

2243
        va_start(ap, path);
203✔
2244
        l = strv_new_ap(path, ap);
203✔
2245
        va_end(ap);
203✔
2246

2247
        if (!l)
203✔
2248
                return -ENOMEM;
203✔
2249

2250
        p = strdup(path);
203✔
2251
        if (!p) {
203✔
2252
                strv_free(l);
×
2253
                return -ENOMEM;
×
2254
        }
2255

2256
        free_and_replace(c->path, p);
203✔
2257

2258
        return strv_free_and_replace(c->argv, l);
203✔
2259
}
2260

2261
int exec_command_append(ExecCommand *c, const char *path, ...) {
292✔
2262
        char **l;
292✔
2263
        va_list ap;
292✔
2264
        int r;
292✔
2265

2266
        assert(c);
292✔
2267
        assert(path);
292✔
2268

2269
        va_start(ap, path);
292✔
2270
        l = strv_new_ap(path, ap);
292✔
2271
        va_end(ap);
292✔
2272

2273
        if (!l)
292✔
2274
                return -ENOMEM;
292✔
2275

2276
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates= */ false);
292✔
2277
        if (r < 0)
292✔
2278
                return r;
×
2279

2280
        return 0;
2281
}
2282

2283
static char *destroy_tree(char *path) {
288✔
2284
        if (!path)
288✔
2285
                return NULL;
2286

2287
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
65✔
2288
                log_debug("Spawning process to nuke '%s'", path);
65✔
2289

2290
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
65✔
2291
        }
2292

2293
        return mfree(path);
65✔
2294
}
2295

2296
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
137,821✔
2297
        assert(rt);
137,821✔
2298

2299
        if (rt->manager)
137,821✔
2300
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
171✔
2301

2302
        rt->id = mfree(rt->id);
137,821✔
2303
        rt->tmp_dir = mfree(rt->tmp_dir);
137,821✔
2304
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
137,821✔
2305
        safe_close_pair(rt->userns_storage_socket);
137,821✔
2306
        safe_close_pair(rt->netns_storage_socket);
137,821✔
2307
        safe_close_pair(rt->ipcns_storage_socket);
137,821✔
2308
}
137,821✔
2309

2310
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
137,787✔
2311
        if (!rt)
137,787✔
2312
                return NULL;
2313

2314
        exec_shared_runtime_done(rt);
137,787✔
2315
        return mfree(rt);
137,787✔
2316
}
2317

2318
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
186✔
2319
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
140,792✔
2320

2321
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
66✔
2322
        if (!rt)
66✔
2323
                return NULL;
2324

2325
        assert(rt->n_ref > 0);
51✔
2326
        rt->n_ref--;
51✔
2327

2328
        if (rt->n_ref > 0)
51✔
2329
                return NULL;
2330

2331
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
51✔
2332
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
51✔
2333

2334
        return exec_shared_runtime_free(rt);
51✔
2335
}
2336

2337
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
137,787✔
2338
        _cleanup_free_ char *id_copy = NULL;
275,574✔
2339
        ExecSharedRuntime *n;
137,787✔
2340

2341
        assert(ret);
137,787✔
2342

2343
        id_copy = strdup(id);
137,787✔
2344
        if (!id_copy)
137,787✔
2345
                return -ENOMEM;
2346

2347
        n = new(ExecSharedRuntime, 1);
137,787✔
2348
        if (!n)
137,787✔
2349
                return -ENOMEM;
2350

2351
        *n = (ExecSharedRuntime) {
137,787✔
2352
                .id = TAKE_PTR(id_copy),
137,787✔
2353
                .userns_storage_socket = EBADF_PAIR,
2354
                .netns_storage_socket = EBADF_PAIR,
2355
                .ipcns_storage_socket = EBADF_PAIR,
2356
        };
2357

2358
        *ret = n;
137,787✔
2359
        return 0;
137,787✔
2360
}
2361

2362
static int exec_shared_runtime_add(
171✔
2363
                Manager *m,
2364
                const char *id,
2365
                char **tmp_dir,
2366
                char **var_tmp_dir,
2367
                int userns_storage_socket[2],
2368
                int netns_storage_socket[2],
2369
                int ipcns_storage_socket[2],
2370
                ExecSharedRuntime **ret) {
2371

2372
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
171✔
2373
        int r;
171✔
2374

2375
        assert(m);
171✔
2376
        assert(id);
171✔
2377

2378
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2379

2380
        r = exec_shared_runtime_allocate(&rt, id);
171✔
2381
        if (r < 0)
171✔
2382
                return r;
2383

2384
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
171✔
2385
        if (r < 0)
171✔
2386
                return r;
2387

2388
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
171✔
2389
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
171✔
2390

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

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

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

2406
        rt->manager = m;
171✔
2407

2408
        if (ret)
171✔
2409
                *ret = rt;
75✔
2410
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2411
        TAKE_PTR(rt);
171✔
2412
        return 0;
171✔
2413
}
2414

2415
static int exec_shared_runtime_make(
7,789✔
2416
                Manager *m,
2417
                const ExecContext *c,
2418
                const char *id,
2419
                ExecSharedRuntime **ret) {
2420

2421
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
7,789✔
2422
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
23,367✔
2423
        int r;
7,789✔
2424

2425
        assert(m);
7,789✔
2426
        assert(c);
7,789✔
2427
        assert(id);
7,789✔
2428

2429
        /* It is not necessary to create ExecSharedRuntime object. */
2430
        if (!c->user_namespace_path && !exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) &&
7,789✔
2431
            c->private_tmp != PRIVATE_TMP_CONNECTED && c->private_var_tmp != PRIVATE_TMP_CONNECTED) {
7,780✔
2432
                *ret = NULL;
7,714✔
2433
                return 0;
7,714✔
2434
        }
2435

2436
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
108✔
2437
            !prefixed_path_strv_contains(c->inaccessible_paths, "/tmp")) {
33✔
2438

2439
                r = setup_tmp_dir_one(id, "/tmp", &tmp_dir);
33✔
2440
                if (r < 0)
33✔
2441
                        return r;
2442
        }
2443

2444
        if (c->private_var_tmp == PRIVATE_TMP_CONNECTED &&
144✔
2445
            !prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") &&
138✔
2446
            !prefixed_path_strv_contains(c->inaccessible_paths, "/var")) {
69✔
2447

2448
                r = setup_tmp_dir_one(id, "/var/tmp", &var_tmp_dir);
69✔
2449
                if (r < 0)
69✔
2450
                        return r;
2451
        }
2452

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

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

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

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

2469
        return 1;
2470
}
2471

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

2476
        assert(m);
7,885✔
2477
        assert(id);
7,885✔
2478
        assert(ret);
7,885✔
2479

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

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

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

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

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

2510
        assert(m);
102✔
2511
        assert(f);
102✔
2512
        assert(fds);
102✔
2513

2514
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
222✔
2515
                fprintf(f, "exec-runtime=%s", rt->id);
120✔
2516

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2583
                fputc('\n', f);
120✔
2584
        }
2585

2586
        return 0;
102✔
2587
}
2588

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

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

2599
        assert(u);
140,621✔
2600
        assert(key);
140,621✔
2601
        assert(value);
140,621✔
2602

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

2610
        if (u->manager) {
140,621✔
2611
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
140,621✔
2612
                        return log_oom();
×
2613

2614
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
140,621✔
2615
        }
2616
        if (!rt) {
140,621✔
2617
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
137,616✔
2618
                        return log_oom();
×
2619

2620
                rt = rt_create;
137,616✔
2621
        }
2622

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

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

2631
        } else if (streq(key, "netns-socket-0")) {
140,621✔
2632

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

2638
        } else if (streq(key, "netns-socket-1")) {
140,621✔
2639

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

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

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

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

2661
        return 1;
2662
}
2663

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

2671
        assert(m);
96✔
2672
        assert(fds);
96✔
2673

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2799
        assert(m);
1,632✔
2800

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

2803
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,728✔
2804
                if (rt->n_ref > 0)
96✔
2805
                        continue;
96✔
2806

2807
                (void) exec_shared_runtime_free(rt);
×
2808
        }
2809
}
1,632✔
2810

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

2822
        assert(unit);
7,885✔
2823
        assert(context);
7,885✔
2824
        assert(ret);
7,885✔
2825

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

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

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

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

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

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

2856
        *ret = TAKE_PTR(rt);
186✔
2857
        return 1;
186✔
2858
}
2859

2860
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
57,721✔
2861
        if (!rt)
57,721✔
2862
                return NULL;
2863

2864
        exec_shared_runtime_unref(rt->shared);
186✔
2865
        dynamic_creds_unref(rt->dynamic_creds);
186✔
2866

2867
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
186✔
2868

2869
        safe_close_pair(rt->ephemeral_storage_socket);
186✔
2870
        return mfree(rt);
186✔
2871
}
2872

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

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

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

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

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

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

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

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

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

2919
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
34✔
2920

2921
        p->cgroup_path = mfree(p->cgroup_path);
34✔
2922

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

2928
        p->received_credentials_directory = mfree(p->received_credentials_directory);
34✔
2929
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
34✔
2930

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

2936
        p->stdin_fd = safe_close(p->stdin_fd);
34✔
2937
        p->stdout_fd = safe_close(p->stdout_fd);
34✔
2938
        p->stderr_fd = safe_close(p->stderr_fd);
34✔
2939

2940
        p->notify_socket = mfree(p->notify_socket);
34✔
2941

2942
        open_file_free_many(&p->open_files);
34✔
2943

2944
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
34✔
2945

2946
        exec_params_shallow_clear(p);
34✔
2947
}
2948

2949
void exec_directory_done(ExecDirectory *d) {
288,445✔
2950
        if (!d)
288,445✔
2951
                return;
2952

2953
        FOREACH_ARRAY(i, d->items, d->n_items) {
290,164✔
2954
                free(i->path);
1,719✔
2955
                strv_free(i->symlinks);
1,719✔
2956
        }
2957

2958
        d->items = mfree(d->items);
288,445✔
2959
        d->n_items = 0;
288,445✔
2960
        d->mode = 0755;
288,445✔
2961
}
2962

2963
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
4,704✔
2964
        assert(d);
4,704✔
2965
        assert(path);
4,704✔
2966

2967
        FOREACH_ARRAY(i, d->items, d->n_items)
7,138✔
2968
                if (path_equal(i->path, path))
2,449✔
2969
                        return i;
2970

2971
        return NULL;
2972
}
2973

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

2980
        assert(d);
4,704✔
2981
        assert(path);
4,704✔
2982

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

2989
                existing->flags |= flags;
15✔
2990

2991
                return 0; /* existing item is updated */
15✔
2992
        }
2993

2994
        p = strdup(path);
4,689✔
2995
        if (!p)
4,689✔
2996
                return -ENOMEM;
2997

2998
        if (symlink) {
4,689✔
2999
                s = strv_new(symlink);
6✔
3000
                if (!s)
6✔
3001
                        return -ENOMEM;
3002
        }
3003

3004
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
4,689✔
3005
                return -ENOMEM;
3006

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

3013
        return 1; /* new item is added */
4,689✔
3014
}
3015

3016
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
925✔
3017
        assert(a);
925✔
3018
        assert(b);
925✔
3019

3020
        return path_compare(a->path, b->path);
925✔
3021
}
3022

3023
void exec_directory_sort(ExecDirectory *d) {
159,246✔
3024
        assert(d);
159,246✔
3025

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

3031
        if (d->n_items <= 1)
159,246✔
3032
                return;
3033

3034
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
165✔
3035

3036
        for (size_t i = 1; i < d->n_items; i++)
720✔
3037
                for (size_t j = 0; j < i; j++)
1,850✔
3038
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,310✔
3039
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3040
                                break;
15✔
3041
                        }
3042
}
3043

3044
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3045
        ExecDirectoryType t;
4✔
3046

3047
        assert(s);
4✔
3048

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

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

3058
        return 1U << t;
2✔
3059
}
3060

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

3072
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,741✔
3073

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

3089
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,732✔
3090

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

3097
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,759✔
3098

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

3105
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
19,753✔
3106

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

3116
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3117

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

UNCOV
3126
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3127

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

3139
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
321✔
3140

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

3147
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
16,322✔
3148

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

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