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

systemd / systemd / 19086794632

04 Nov 2025 08:16PM UTC coverage: 72.165% (-0.1%) from 72.279%
19086794632

push

github

yuwata
TEST-64-UDEV-STORAGE: generate debugging logs

Let's see if it provides something useful for debugging issue #39544.

305891 of 423878 relevant lines covered (72.16%)

1224411.04 hits per line

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

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

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

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

68
const char* exec_context_tty_path(const ExecContext *context) {
15,041✔
69
        assert(context);
15,041✔
70

71
        if (context->stdio_as_fds)
15,041✔
72
                return NULL;
73

74
        if (context->tty_path)
14,264✔
75
                return context->tty_path;
697✔
76

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

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

86
        unsigned rows, cols;
1,499✔
87
        int r;
1,499✔
88

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

93
        if (!isatty_safe(output_fd))
1,499✔
94
                return 0;
1,499✔
95

96
        if (!tty_path)
880✔
97
                tty_path = exec_context_tty_path(context);
442✔
98

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

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

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

122
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
880✔
123
}
124

125
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
13,718✔
126
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
27,436✔
127
        int fd, r;
13,718✔
128

129
        assert(context);
13,718✔
130

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

136
        const char *path = exec_context_tty_path(context);
13,718✔
137

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

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

158
        if (context->tty_reset)
459✔
159
                (void) terminal_reset_defensive(
400✔
160
                                fd,
161
                                TERMINAL_RESET_SWITCH_TO_TEXT |
162
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
397✔
163

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

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

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

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

185
        if (context->tty_vhangup)
459✔
186
                (void) terminal_vhangup_fd(fd);
173✔
187

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

192
        if (context->tty_vt_disallocate && path)
459✔
193
                (void) vt_disallocate(path);
93✔
194
}
195

196
bool exec_needs_network_namespace(const ExecContext *context) {
60,419✔
197
        assert(context);
60,419✔
198

199
        return context->private_network || context->network_namespace_path;
60,419✔
200
}
201

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

206
bool exec_needs_ipc_namespace(const ExecContext *context) {
54,710✔
207
        assert(context);
54,710✔
208

209
        return context->private_ipc || context->ipc_namespace_path;
54,710✔
210
}
211

212
static bool needs_cgroup_namespace(ProtectControlGroups i) {
141,592✔
213
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
141,592✔
214
}
215

216
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
86,874✔
217
        assert(context);
86,874✔
218

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

232
bool exec_needs_cgroup_namespace(const ExecContext *context) {
54,718✔
233
        assert(context);
54,718✔
234

235
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
54,718✔
236
}
237

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

241
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
28,101✔
242
}
243

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

247
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,028✔
248
}
249

250
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
77,573✔
251
        assert(context);
77,573✔
252

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

257
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
69,901✔
258
}
259

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

265
        assert(context);
33,680✔
266

267
        if (context->root_image)
33,680✔
268
                return true;
269

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

273
        if (!strv_isempty(context->read_write_paths) ||
33,629✔
274
            !strv_isempty(context->read_only_paths) ||
31,476✔
275
            !strv_isempty(context->inaccessible_paths) ||
31,463✔
276
            !strv_isempty(context->exec_paths) ||
31,438✔
277
            !strv_isempty(context->no_exec_paths))
31,438✔
278
                return true;
279

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

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

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

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

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

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

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

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

304
        if (context->private_devices ||
29,806✔
305
            context->private_mounts > 0 ||
29,423✔
306
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,946✔
307
            context->protect_system != PROTECT_SYSTEM_NO ||
28,917✔
308
            context->protect_home != PROTECT_HOME_NO ||
28,917✔
309
            context->protect_kernel_tunables ||
28,917✔
310
            context->protect_kernel_modules ||
28,917✔
311
            context->protect_kernel_logs ||
53,595✔
312
            exec_needs_cgroup_mount(context) ||
26,796✔
313
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,778✔
314
            context->proc_subset != PROC_SUBSET_ALL ||
26,724✔
315
            context->private_bpf != PRIVATE_BPF_NO ||
53,418✔
316
            exec_needs_ipc_namespace(context) ||
53,418✔
317
            exec_needs_pid_namespace(context, params))
26,709✔
318
                return true;
3,144✔
319

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

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

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

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

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

342
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
159,598✔
343
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
138,120✔
344
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,173✔
345
                                return true;
346

347
        return false;
348
}
349

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

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

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

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

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

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

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

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

375
        if (params->debug_invocation)
24,498✔
376
                return LOG_DEBUG;
377

378
        return exec_log_level_max(context);
24,494✔
379
}
380

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

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

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

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

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

398
        return true;
399
}
400

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

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

412
        const char *subgroup = NULL;
12,963✔
413
        char *p;
12,963✔
414

415
        assert(params);
12,963✔
416
        assert(c);
12,963✔
417
        assert(ret);
12,963✔
418

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

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

436
        if (subgroup)
670✔
437
                p = path_join(prefix, subgroup);
722✔
438
        else
439
                p = strdup(strempty(prefix));
12,249✔
440
        if (!p)
12,963✔
441
                return -ENOMEM;
442

443
        *ret = p;
12,963✔
444
        return !!subgroup;
12,963✔
445
}
446

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

450
        return c->cpu_affinity_from_numa;
1,317✔
451
}
452

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

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

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

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

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

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

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

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

497
        LOG_CONTEXT_PUSH_UNIT(unit);
4,526✔
498

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

623
void exec_context_init(ExecContext *c) {
61,236✔
624
        assert(c);
61,236✔
625

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

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

656
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
367,416✔
657
                d->mode = 0755;
306,180✔
658

659
        numa_policy_reset(&c->numa_policy);
61,236✔
660

661
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
61,236✔
662
}
61,236✔
663

664
void exec_context_done(ExecContext *c) {
49,020✔
665
        assert(c);
49,020✔
666

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

672
        rlimit_free_all(c->rlimit);
49,020✔
673

674
        for (size_t l = 0; l < 3; l++) {
196,080✔
675
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
147,060✔
676
                c->stdio_file[l] = mfree(c->stdio_file[l]);
147,060✔
677
        }
678

679
        c->working_directory = mfree(c->working_directory);
49,020✔
680
        c->root_directory = mfree(c->root_directory);
49,020✔
681
        c->root_image = mfree(c->root_image);
49,020✔
682
        c->root_image_options = mount_options_free_all(c->root_image_options);
49,020✔
683
        c->root_hash = mfree(c->root_hash);
49,020✔
684
        c->root_hash_size = 0;
49,020✔
685
        c->root_hash_path = mfree(c->root_hash_path);
49,020✔
686
        c->root_hash_sig = mfree(c->root_hash_sig);
49,020✔
687
        c->root_hash_sig_size = 0;
49,020✔
688
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
49,020✔
689
        c->root_verity = mfree(c->root_verity);
49,020✔
690
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
49,020✔
691
        c->extension_directories = strv_free(c->extension_directories);
49,020✔
692
        c->tty_path = mfree(c->tty_path);
49,020✔
693
        c->syslog_identifier = mfree(c->syslog_identifier);
49,020✔
694
        c->user = mfree(c->user);
49,020✔
695
        c->group = mfree(c->group);
49,020✔
696

697
        c->supplementary_groups = strv_free(c->supplementary_groups);
49,020✔
698

699
        c->pam_name = mfree(c->pam_name);
49,020✔
700

701
        c->read_only_paths = strv_free(c->read_only_paths);
49,020✔
702
        c->read_write_paths = strv_free(c->read_write_paths);
49,020✔
703
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
49,020✔
704
        c->exec_paths = strv_free(c->exec_paths);
49,020✔
705
        c->no_exec_paths = strv_free(c->no_exec_paths);
49,020✔
706
        c->exec_search_path = strv_free(c->exec_search_path);
49,020✔
707

708
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
49,020✔
709
        c->bind_mounts = NULL;
49,020✔
710
        c->n_bind_mounts = 0;
49,020✔
711
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
49,020✔
712
        c->temporary_filesystems = NULL;
49,020✔
713
        c->n_temporary_filesystems = 0;
49,020✔
714
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
49,020✔
715

716
        cpu_set_done(&c->cpu_set);
49,020✔
717
        numa_policy_reset(&c->numa_policy);
49,020✔
718

719
        c->utmp_id = mfree(c->utmp_id);
49,020✔
720
        c->selinux_context = mfree(c->selinux_context);
49,020✔
721
        c->apparmor_profile = mfree(c->apparmor_profile);
49,020✔
722
        c->smack_process_label = mfree(c->smack_process_label);
49,020✔
723

724
        c->restrict_filesystems = set_free(c->restrict_filesystems);
49,020✔
725

726
        c->syscall_filter = hashmap_free(c->syscall_filter);
49,020✔
727
        c->syscall_archs = set_free(c->syscall_archs);
49,020✔
728
        c->syscall_log = hashmap_free(c->syscall_log);
49,020✔
729
        c->address_families = set_free(c->address_families);
49,020✔
730

731
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
294,120✔
732
                exec_directory_done(d);
245,100✔
733

734
        c->log_level_max = -1;
49,020✔
735

736
        exec_context_free_log_extra_fields(c);
49,020✔
737
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
49,020✔
738
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
49,020✔
739

740
        c->log_ratelimit = (RateLimit) {};
49,020✔
741

742
        c->stdin_data = mfree(c->stdin_data);
49,020✔
743
        c->stdin_data_size = 0;
49,020✔
744

745
        c->user_namespace_path = mfree(c->user_namespace_path);
49,020✔
746
        c->network_namespace_path = mfree(c->network_namespace_path);
49,020✔
747
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
49,020✔
748

749
        c->log_namespace = mfree(c->log_namespace);
49,020✔
750

751
        c->load_credentials = hashmap_free(c->load_credentials);
49,020✔
752
        c->set_credentials = hashmap_free(c->set_credentials);
49,020✔
753
        c->import_credentials = ordered_set_free(c->import_credentials);
49,020✔
754

755
        c->root_image_policy = image_policy_free(c->root_image_policy);
49,020✔
756
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
49,020✔
757
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
49,020✔
758

759
        c->private_hostname = mfree(c->private_hostname);
49,020✔
760
}
49,020✔
761

762
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,864✔
763
        assert(c);
5,864✔
764

765
        if (!runtime_prefix)
5,864✔
766
                return 0;
767

768
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,865✔
769
                _cleanup_free_ char *p = NULL;
1✔
770

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

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

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

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

792
                        (void) unlink(symlink_abs);
×
793
                }
794
        }
795

796
        return 0;
797
}
798

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

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

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

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

813
        return 0;
814
}
815

816
void exec_command_done(ExecCommand *c) {
103,419✔
817
        assert(c);
103,419✔
818

819
        c->path = mfree(c->path);
103,419✔
820
        c->argv = strv_free(c->argv);
103,419✔
821
}
103,419✔
822

823
void exec_command_done_array(ExecCommand *c, size_t n) {
28,911✔
824
        FOREACH_ARRAY(i, c, n)
115,642✔
825
                exec_command_done(i);
86,731✔
826
}
28,911✔
827

828
ExecCommand* exec_command_free(ExecCommand *c) {
16,656✔
829
        if (!c)
16,656✔
830
                return NULL;
831

832
        exec_command_done(c);
16,656✔
833
        return mfree(c);
16,656✔
834
}
835

836
ExecCommand* exec_command_free_list(ExecCommand *c) {
141,789✔
837
        ExecCommand *i;
141,789✔
838

839
        while ((i = LIST_POP(command, c)))
158,445✔
840
                exec_command_free(i);
16,656✔
841

842
        return NULL;
141,789✔
843
}
844

845
void exec_command_free_array(ExecCommand **c, size_t n) {
20,077✔
846
        FOREACH_ARRAY(i, c, n)
161,850✔
847
                *i = exec_command_free_list(*i);
141,773✔
848
}
20,077✔
849

850
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,404✔
851
        FOREACH_ARRAY(i, c, n)
29,615✔
852
                exec_status_reset(&i->exec_status);
22,211✔
853
}
7,404✔
854

855
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,144✔
856
        FOREACH_ARRAY(i, c, n)
36,588✔
857
                LIST_FOREACH(command, z, *i)
33,616✔
858
                        exec_status_reset(&z->exec_status);
2,172✔
859
}
5,144✔
860

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

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

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

872
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
40,698✔
873
        assert(c);
40,698✔
874

875
        switch (fd_index) {
40,698✔
876

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

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

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

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

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

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

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

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

904
        assert(c);
2,263✔
905
        assert(ret);
2,263✔
906

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

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

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

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

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

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

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

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

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

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

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

966
        *ret = TAKE_PTR(v);
2,263✔
967

968
        return 0;
2,263✔
969
}
970

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

974
        if (!tty)
227✔
975
                return true;
976

977
        tty = skip_dev_prefix(tty);
227✔
978

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

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

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

990
static bool exec_context_may_touch_tty(const ExecContext *ec) {
24,127✔
991
        assert(ec);
24,127✔
992

993
        return ec->tty_reset ||
48,039✔
994
                ec->tty_vhangup ||
23,912✔
995
                ec->tty_vt_disallocate ||
23,912✔
996
                exec_input_is_terminal(ec->std_input) ||
23,912✔
997
                ec->std_output == EXEC_OUTPUT_TTY ||
48,039✔
998
                ec->std_error == EXEC_OUTPUT_TTY;
23,863✔
999
}
1000

1001
bool exec_context_may_touch_console(const ExecContext *ec) {
22,657✔
1002

1003
        return exec_context_may_touch_tty(ec) &&
22,884✔
1004
               tty_may_match_dev_console(exec_context_tty_path(ec));
227✔
1005
}
1006

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

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

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

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

1026
static void strv_fprintf(FILE *f, char **l) {
×
1027
        assert(f);
×
1028

1029
        STRV_FOREACH(g, l)
×
1030
                fprintf(f, " %s", *g);
×
1031
}
×
1032

1033
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,776✔
1034
        assert(f);
1,776✔
1035
        assert(prefix);
1,776✔
1036
        assert(name);
1,776✔
1037

1038
        if (!strv_isempty(strv)) {
1,776✔
1039
                fprintf(f, "%s%s:", prefix, name);
×
1040
                strv_fprintf(f, strv);
×
1041
                fputs("\n", f);
×
1042
        }
1043
}
1,776✔
1044

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

1049
        prefix = strempty(prefix);
×
1050

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

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

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

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

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

1090
        assert(c);
222✔
1091
        assert(f);
222✔
1092

1093
        prefix = strempty(prefix);
222✔
1094

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

1153
        if (c->private_bpf == PRIVATE_BPF_YES) {
222✔
1154
                _cleanup_free_ char
×
1155
                        *commands = bpf_delegate_commands_to_string(c->bpf_delegate_commands),
×
1156
                        *maps = bpf_delegate_maps_to_string(c->bpf_delegate_maps),
×
1157
                        *programs = bpf_delegate_programs_to_string(c->bpf_delegate_programs),
×
1158
                        *attachments = bpf_delegate_attachments_to_string(c->bpf_delegate_attachments);
×
1159

1160
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
1161
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
1162
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
1163
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1164
        }
1165

1166
        if (c->set_login_environment >= 0)
222✔
1167
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1168

1169
        if (c->root_image)
222✔
1170
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1171

1172
        if (c->root_image_options) {
222✔
1173
                fprintf(f, "%sRootImageOptions:", prefix);
×
1174
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1175
                        if (!isempty(o->options))
×
1176
                                fprintf(f, " %s:%s",
×
1177
                                        partition_designator_to_string(o->partition_designator),
1178
                                        o->options);
1179
                fprintf(f, "\n");
×
1180
        }
1181

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1506
                                if (first)
4,268✔
1507
                                        first = false;
1508
                                else
1509
                                        fputc(' ', f);
4,257✔
1510

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

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

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

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

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

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

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

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

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

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

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

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

1581
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
222✔
1582
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1583
                        mount->ignore_enoent ? "-": "",
×
1584
                        mount->source,
1585
                        mount->destination);
1586
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1587
                        fprintf(f, ":%s:%s",
×
1588
                                partition_designator_to_string(o->partition_designator),
1589
                                strempty(o->options));
×
1590
                fprintf(f, "\n");
×
1591
        }
1592

1593
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
222✔
1594
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1595
                        mount->ignore_enoent ? "-": "",
×
1596
                        mount->source);
1597
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1598
                        fprintf(f, ":%s:%s",
×
1599
                                partition_designator_to_string(o->partition_designator),
1600
                                strempty(o->options));
×
1601
                fprintf(f, "\n");
×
1602
        }
1603

1604
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
222✔
1605
}
222✔
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) {
2,634✔
1623
        int p;
2,634✔
1624

1625
        assert(c);
2,634✔
1626

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

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

1634
        return ioprio_normalize(p);
2,620✔
1635
}
1636

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

1640
        /* Explicit setting wins */
1641
        if (c->mount_apivfs >= 0)
35,811✔
1642
                return c->mount_apivfs > 0;
107✔
1643

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

1648
        return false;
1649
}
1650

1651
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
30,223✔
1652
        assert(c);
30,223✔
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)
30,223✔
1657
                return true;
1658

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

1662
        if (exec_context_get_effective_mount_apivfs(c))
30,213✔
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)
30,115✔
1667
                return true;
×
1668

1669
        return false;
1670
}
1671

1672
void exec_context_free_log_extra_fields(ExecContext *c) {
49,022✔
1673
        assert(c);
49,022✔
1674

1675
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
49,027✔
1676
                free(field->iov_base);
5✔
1677

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

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

1688
        assert(c);
1,470✔
1689

1690
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1691
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,470✔
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,470✔
1697
                return;
1698

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

1703
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
37✔
1704
        if (fd < 0)
37✔
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)
37✔
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))
37✔
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);
37✔
1721
        if (r < 0)
37✔
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);
37✔
1723
}
1724

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

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

1734
        assert(c);
×
1735
        assert(prefix);
×
1736
        assert(ret);
×
1737

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

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

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

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

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

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

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

1767
                        STRV_FOREACH(symlink, i->symlinks) {
×
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);
×
1780
        return 0;
×
1781
}
1782

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

1786
        assert(c);
1,301✔
1787
        assert(ret);
1,301✔
1788

1789
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,806✔
1790
                if (c->directories[t].n_items > 0)
6,505✔
1791
                        mask |= 1U << t;
314✔
1792

1793
        *ret = mask;
1,301✔
1794
        return 0;
1,301✔
1795
}
1796

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

1800
        assert(c);
1,317✔
1801

1802
        if (c->oom_score_adjust_set)
1,317✔
1803
                return c->oom_score_adjust;
495✔
1804

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

1809
        return n;
822✔
1810
}
1811

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

1817
        assert(c);
1,317✔
1818

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

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

1831
        return n;
1,317✔
1832
}
1833

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

1837
        assert(c);
1,317✔
1838

1839
        if (c->nice_set)
1,317✔
1840
                return c->nice;
4✔
1841

1842
        errno = 0;
1,313✔
1843
        n = getpriority(PRIO_PROCESS, 0);
1,313✔
1844
        if (errno > 0) {
1,313✔
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) {
1,317✔
1853
        int n;
1,317✔
1854

1855
        assert(c);
1,317✔
1856

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

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

1864
        return n < 0 ? SCHED_OTHER : n;
1,317✔
1865
}
1866

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

1871
        assert(c);
1,317✔
1872

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

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

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

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

1886
        assert(c);
1,317✔
1887

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

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

1895
        return (uint64_t) MAX(r, 0);
1,317✔
1896
}
1897

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

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

1904
        return c->user || c->dynamic_user || c->pam_name;
20,620✔
1905
}
1906

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

1910
        assert(c);
1,317✔
1911

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

1916
        void *id, *val;
1,317✔
1917
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
13,440✔
1918
                _cleanup_free_ char *name = NULL;
12,123✔
1919
                const char *e = NULL;
12,123✔
1920
                char *s;
12,123✔
1921
                int num = PTR_TO_INT(val);
12,123✔
1922

1923
                if (c->syscall_allow_list && num >= 0)
12,123✔
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);
12,123✔
1928
                if (!name)
12,123✔
1929
                        continue;
×
1930

1931
                if (num >= 0) {
12,123✔
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);
12,123✔
1943

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

1948
        strv_sort(l);
1,317✔
1949
#endif
1950

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

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

1957
        assert(c);
1,317✔
1958

1959
#if HAVE_SECCOMP
1960
        void *id;
1,317✔
1961
        SET_FOREACH(id, c->syscall_archs) {
1,352✔
1962
                const char *name;
35✔
1963

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

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

1972
        strv_sort(l);
1,317✔
1973
#endif
1974

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

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

1981
        assert(c);
1,317✔
1982

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

1987
        void *id, *val;
1,317✔
1988
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,317✔
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);
1,317✔
2000
#endif
2001

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

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

2009
        assert(c);
1,317✔
2010

2011
        SET_FOREACH(af, c->address_families) {
1,430✔
2012
                const char *name;
113✔
2013

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

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

2022
        strv_sort(l);
1,317✔
2023

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

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

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

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

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

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

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

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

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

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

2059
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd;
66,080✔
2060
}
2061

2062
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2063
        int r;
4✔
2064

2065
        assert(context);
4✔
2066

2067
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
4✔
2068
                r = path_uses_vpick(mi->source);
×
2069
                if (r != 0)
×
2070
                        return r;
2071
        }
2072
        STRV_FOREACH(ed, context->extension_directories) {
4✔
2073
                r = path_uses_vpick(*ed);
×
2074
                if (r != 0)
×
2075
                        return r;
2076
        }
2077

2078
        return 0;
2079
}
2080

2081
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,442✔
2082
        assert(s);
4,442✔
2083

2084
        *s = (ExecStatus) {
4,442✔
2085
                .pid = pid,
2086
        };
2087

2088
        if (ts)
4,442✔
2089
                s->start_timestamp = *ts;
4,442✔
2090
        else
2091
                dual_timestamp_now(&s->start_timestamp);
×
2092
}
4,442✔
2093

2094
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,085✔
2095
        assert(s);
2,085✔
2096

2097
        if (s->pid != pid)
2,085✔
2098
                *s = (ExecStatus) {
6✔
2099
                        .pid = pid,
2100
                };
2101

2102
        dual_timestamp_now(&s->exit_timestamp);
2,085✔
2103

2104
        s->code = code;
2,085✔
2105
        s->status = status;
2,085✔
2106

2107
        if (context && context->utmp_id)
2,085✔
2108
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2109
}
2,085✔
2110

2111
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,782✔
2112
        assert(s);
7,782✔
2113
        assert(ucred);
7,782✔
2114
        assert(ts);
7,782✔
2115

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

2121
        s->handoff_timestamp = *ts;
7,782✔
2122
}
7,782✔
2123

2124
void exec_status_reset(ExecStatus *s) {
26,291✔
2125
        assert(s);
26,291✔
2126

2127
        *s = (ExecStatus) {};
26,291✔
2128
}
26,291✔
2129

2130
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
98✔
2131
        assert(s);
98✔
2132
        assert(f);
98✔
2133

2134
        if (s->pid <= 0)
98✔
2135
                return;
2136

2137
        prefix = strempty(prefix);
10✔
2138

2139
        fprintf(f,
10✔
2140
                "%sPID: "PID_FMT"\n",
2141
                prefix, s->pid);
2142

2143
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2144
                fprintf(f,
10✔
2145
                        "%sStart Timestamp: %s\n",
2146
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2147

2148
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2149
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2150
                fprintf(f,
10✔
2151
                        "%sHandoff Timestamp: %s since start\n",
2152
                        prefix,
2153
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2154
        else
2155
                fprintf(f,
×
2156
                        "%sHandoff Timestamp: %s\n",
2157
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2158

2159
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2160

2161
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
2162
                        fprintf(f,
×
2163
                                "%sExit Timestamp: %s since handoff\n",
2164
                                prefix,
2165
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
2166
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2167
                        fprintf(f,
×
2168
                                "%sExit Timestamp: %s since start\n",
2169
                                prefix,
2170
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2171
                else
2172
                        fprintf(f,
×
2173
                                "%sExit Timestamp: %s\n",
2174
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2175

2176
                fprintf(f,
×
2177
                        "%sExit Code: %s\n"
2178
                        "%sExit Status: %i\n",
2179
                        prefix, sigchld_code_to_string(s->code),
×
2180
                        prefix, s->status);
×
2181
        }
2182
}
2183

2184
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
98✔
2185
        _cleanup_free_ char *cmd = NULL;
196✔
2186
        const char *prefix2;
98✔
2187

2188
        assert(c);
98✔
2189
        assert(f);
98✔
2190

2191
        prefix = strempty(prefix);
98✔
2192
        prefix2 = strjoina(prefix, "\t");
490✔
2193

2194
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
98✔
2195

2196
        fprintf(f,
98✔
2197
                "%sCommand Line: %s\n",
2198
                prefix, strnull(cmd));
2199

2200
        exec_status_dump(&c->exec_status, f, prefix2);
98✔
2201
}
98✔
2202

2203
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
95✔
2204
        assert(f);
95✔
2205

2206
        prefix = strempty(prefix);
95✔
2207

2208
        LIST_FOREACH(command, i, c)
193✔
2209
                exec_command_dump(i, f, prefix);
98✔
2210
}
95✔
2211

2212
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
16,656✔
2213
        ExecCommand *end;
16,656✔
2214

2215
        assert(l);
16,656✔
2216
        assert(e);
16,656✔
2217

2218
        if (*l) {
16,656✔
2219
                /* It's kind of important, that we keep the order here */
2220
                end = LIST_FIND_TAIL(command, *l);
366✔
2221
                LIST_INSERT_AFTER(command, *l, end, e);
129✔
2222
        } else
2223
                *l = e;
16,527✔
2224
}
16,656✔
2225

2226
int exec_command_set(ExecCommand *c, const char *path, ...) {
218✔
2227
        va_list ap;
218✔
2228
        char **l, *p;
218✔
2229

2230
        assert(c);
218✔
2231
        assert(path);
218✔
2232

2233
        va_start(ap, path);
218✔
2234
        l = strv_new_ap(path, ap);
218✔
2235
        va_end(ap);
218✔
2236

2237
        if (!l)
218✔
2238
                return -ENOMEM;
218✔
2239

2240
        p = strdup(path);
218✔
2241
        if (!p) {
218✔
2242
                strv_free(l);
×
2243
                return -ENOMEM;
×
2244
        }
2245

2246
        free_and_replace(c->path, p);
218✔
2247

2248
        return strv_free_and_replace(c->argv, l);
218✔
2249
}
2250

2251
int exec_command_append(ExecCommand *c, const char *path, ...) {
325✔
2252
        char **l;
325✔
2253
        va_list ap;
325✔
2254
        int r;
325✔
2255

2256
        assert(c);
325✔
2257
        assert(path);
325✔
2258

2259
        va_start(ap, path);
325✔
2260
        l = strv_new_ap(path, ap);
325✔
2261
        va_end(ap);
325✔
2262

2263
        if (!l)
325✔
2264
                return -ENOMEM;
325✔
2265

2266
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
325✔
2267
        if (r < 0)
325✔
2268
                return r;
×
2269

2270
        return 0;
2271
}
2272

2273
static char *destroy_tree(char *path) {
260✔
2274
        if (!path)
260✔
2275
                return NULL;
2276

2277
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
94✔
2278
                log_debug("Spawning process to nuke '%s'", path);
94✔
2279

2280
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
94✔
2281
        }
2282

2283
        return mfree(path);
94✔
2284
}
2285

2286
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
95,244✔
2287
        assert(rt);
95,244✔
2288

2289
        if (rt->manager)
95,244✔
2290
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
153✔
2291

2292
        rt->id = mfree(rt->id);
95,244✔
2293
        rt->tmp_dir = mfree(rt->tmp_dir);
95,244✔
2294
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
95,244✔
2295
        safe_close_pair(rt->userns_storage_socket);
95,244✔
2296
        safe_close_pair(rt->netns_storage_socket);
95,244✔
2297
        safe_close_pair(rt->ipcns_storage_socket);
95,244✔
2298
}
95,244✔
2299

2300
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
95,212✔
2301
        if (!rt)
95,212✔
2302
                return NULL;
2303

2304
        exec_shared_runtime_done(rt);
95,212✔
2305
        return mfree(rt);
95,212✔
2306
}
2307

2308
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
154✔
2309
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
97,684✔
2310

2311
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
54✔
2312
        if (!rt)
54✔
2313
                return NULL;
2314

2315
        assert(rt->n_ref > 0);
53✔
2316
        rt->n_ref--;
53✔
2317

2318
        if (rt->n_ref > 0)
53✔
2319
                return NULL;
2320

2321
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
53✔
2322
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
53✔
2323

2324
        return exec_shared_runtime_free(rt);
53✔
2325
}
2326

2327
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
95,212✔
2328
        _cleanup_free_ char *id_copy = NULL;
190,424✔
2329
        ExecSharedRuntime *n;
95,212✔
2330

2331
        assert(ret);
95,212✔
2332

2333
        id_copy = strdup(id);
95,212✔
2334
        if (!id_copy)
95,212✔
2335
                return -ENOMEM;
2336

2337
        n = new(ExecSharedRuntime, 1);
95,212✔
2338
        if (!n)
95,212✔
2339
                return -ENOMEM;
2340

2341
        *n = (ExecSharedRuntime) {
95,212✔
2342
                .id = TAKE_PTR(id_copy),
95,212✔
2343
                .userns_storage_socket = EBADF_PAIR,
2344
                .netns_storage_socket = EBADF_PAIR,
2345
                .ipcns_storage_socket = EBADF_PAIR,
2346
        };
2347

2348
        *ret = n;
95,212✔
2349
        return 0;
95,212✔
2350
}
2351

2352
static int exec_shared_runtime_add(
153✔
2353
                Manager *m,
2354
                const char *id,
2355
                char **tmp_dir,
2356
                char **var_tmp_dir,
2357
                int userns_storage_socket[2],
2358
                int netns_storage_socket[2],
2359
                int ipcns_storage_socket[2],
2360
                ExecSharedRuntime **ret) {
2361

2362
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
153✔
2363
        int r;
153✔
2364

2365
        assert(m);
153✔
2366
        assert(id);
153✔
2367

2368
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2369

2370
        r = exec_shared_runtime_allocate(&rt, id);
153✔
2371
        if (r < 0)
153✔
2372
                return r;
2373

2374
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
153✔
2375
        if (r < 0)
153✔
2376
                return r;
2377

2378
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
153✔
2379
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
153✔
2380
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
153✔
2381

2382
        if (userns_storage_socket) {
153✔
2383
                rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
153✔
2384
                rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
153✔
2385
        }
2386

2387
        if (netns_storage_socket) {
153✔
2388
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
153✔
2389
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
153✔
2390
        }
2391

2392
        if (ipcns_storage_socket) {
153✔
2393
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
153✔
2394
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
153✔
2395
        }
2396

2397
        rt->manager = m;
153✔
2398

2399
        if (ret)
153✔
2400
                *ret = rt;
74✔
2401
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2402
        TAKE_PTR(rt);
153✔
2403
        return 0;
153✔
2404
}
2405

2406
static int exec_shared_runtime_make(
5,599✔
2407
                Manager *m,
2408
                const ExecContext *c,
2409
                const char *id,
2410
                ExecSharedRuntime **ret) {
2411

2412
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
5,599✔
2413
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
16,797✔
2414
        int r;
5,599✔
2415

2416
        assert(m);
5,599✔
2417
        assert(c);
5,599✔
2418
        assert(id);
5,599✔
2419

2420
        /* It is not necessary to create ExecSharedRuntime object. */
2421
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
5,599✔
2422
                *ret = NULL;
5,525✔
2423
                return 0;
5,525✔
2424
        }
2425

2426
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
142✔
2427
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
68✔
2428
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2429
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2430
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
68✔
2431
                if (r < 0)
68✔
2432
                        return r;
2433
        }
2434

2435
        if (c->user_namespace_path)
74✔
2436
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2437
                        return -errno;
×
2438

2439
        if (exec_needs_network_namespace(c))
74✔
2440
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2441
                        return -errno;
×
2442

2443
        if (exec_needs_ipc_namespace(c))
74✔
2444
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2445
                        return -errno;
×
2446

2447
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
74✔
2448
        if (r < 0)
74✔
2449
                return r;
×
2450

2451
        return 1;
2452
}
2453

2454
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
5,678✔
2455
        ExecSharedRuntime *rt;
5,678✔
2456
        int r;
5,678✔
2457

2458
        assert(m);
5,678✔
2459
        assert(id);
5,678✔
2460
        assert(ret);
5,678✔
2461

2462
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
5,678✔
2463
        if (rt)
5,678✔
2464
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2465
                goto ref;
79✔
2466

2467
        if (!create) {
5,599✔
2468
                *ret = NULL;
×
2469
                return 0;
×
2470
        }
2471

2472
        /* If not found, then create a new object. */
2473
        r = exec_shared_runtime_make(m, c, id, &rt);
5,599✔
2474
        if (r < 0)
5,599✔
2475
                return r;
2476
        if (r == 0) {
5,599✔
2477
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2478
                *ret = NULL;
5,525✔
2479
                return 0;
5,525✔
2480
        }
2481

2482
ref:
74✔
2483
        /* increment reference counter. */
2484
        rt->n_ref++;
153✔
2485
        *ret = rt;
153✔
2486
        return 1;
153✔
2487
}
2488

2489
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
63✔
2490
        ExecSharedRuntime *rt;
63✔
2491

2492
        assert(m);
63✔
2493
        assert(f);
63✔
2494
        assert(fds);
63✔
2495

2496
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
163✔
2497
                fprintf(f, "exec-runtime=%s", rt->id);
100✔
2498

2499
                if (rt->tmp_dir)
100✔
2500
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
100✔
2501

2502
                if (rt->var_tmp_dir)
100✔
2503
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
100✔
2504

2505
                if (rt->userns_storage_socket[0] >= 0) {
100✔
2506
                        int copy;
×
2507

2508
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
2509
                        if (copy < 0)
×
2510
                                return copy;
×
2511

2512
                        fprintf(f, " userns-socket-0=%i", copy);
×
2513
                }
2514

2515
                if (rt->userns_storage_socket[1] >= 0) {
100✔
2516
                        int copy;
×
2517

2518
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
2519
                        if (copy < 0)
×
2520
                                return copy;
2521

2522
                        fprintf(f, " userns-socket-1=%i", copy);
×
2523
                }
2524

2525
                if (rt->netns_storage_socket[0] >= 0) {
100✔
2526
                        int copy;
2✔
2527

2528
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2529
                        if (copy < 0)
2✔
2530
                                return copy;
2531

2532
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2533
                }
2534

2535
                if (rt->netns_storage_socket[1] >= 0) {
100✔
2536
                        int copy;
2✔
2537

2538
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2539
                        if (copy < 0)
2✔
2540
                                return copy;
2541

2542
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2543
                }
2544

2545
                if (rt->ipcns_storage_socket[0] >= 0) {
100✔
2546
                        int copy;
×
2547

2548
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2549
                        if (copy < 0)
×
2550
                                return copy;
2551

2552
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2553
                }
2554

2555
                if (rt->ipcns_storage_socket[1] >= 0) {
100✔
2556
                        int copy;
×
2557

2558
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2559
                        if (copy < 0)
×
2560
                                return copy;
2561

2562
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2563
                }
2564

2565
                fputc('\n', f);
100✔
2566
        }
2567

2568
        return 0;
63✔
2569
}
2570

2571
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
97,531✔
2572
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
97,531✔
2573
        ExecSharedRuntime *rt = NULL;
97,531✔
2574
        int r;
97,531✔
2575

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

2581
        assert(u);
97,531✔
2582
        assert(key);
97,531✔
2583
        assert(value);
97,531✔
2584

2585
        /* Manager manages ExecSharedRuntime objects by the unit id.
2586
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2587
        if (isempty(u->id)) {
97,531✔
2588
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2589
                return 0;
×
2590
        }
2591

2592
        if (u->manager) {
97,531✔
2593
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
97,531✔
2594
                        return log_oom();
×
2595

2596
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
97,531✔
2597
        }
2598
        if (!rt) {
97,531✔
2599
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
95,059✔
2600
                        return log_oom();
×
2601

2602
                rt = rt_create;
95,059✔
2603
        }
2604

2605
        if (streq(key, "tmp-dir")) {
97,531✔
2606
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2607
                        return -ENOMEM;
2608

2609
        } else if (streq(key, "var-tmp-dir")) {
97,531✔
2610
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2611
                        return -ENOMEM;
2612

2613
        } else if (streq(key, "netns-socket-0")) {
97,531✔
2614

2615
                safe_close(rt->netns_storage_socket[0]);
×
2616
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2617
                if (rt->netns_storage_socket[0] < 0)
×
2618
                        return 0;
2619

2620
        } else if (streq(key, "netns-socket-1")) {
97,531✔
2621

2622
                safe_close(rt->netns_storage_socket[1]);
×
2623
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2624
                if (rt->netns_storage_socket[1] < 0)
×
2625
                        return 0;
2626
        } else
2627
                return 0;
2628

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

2637
                rt_create->manager = u->manager;
×
2638

2639
                /* Avoid cleanup */
2640
                TAKE_PTR(rt_create);
×
2641
        }
2642

2643
        return 1;
2644
}
2645

2646
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
79✔
2647
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
79✔
2648
        char *id = NULL;
79✔
2649
        int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
79✔
2650
        const char *p, *v = ASSERT_PTR(value);
79✔
2651
        size_t n;
79✔
2652

2653
        assert(m);
79✔
2654
        assert(fds);
79✔
2655

2656
        n = strcspn(v, " ");
79✔
2657
        id = strndupa_safe(v, n);
79✔
2658
        if (v[n] != ' ')
79✔
2659
                goto finalize;
×
2660
        p = v + n + 1;
79✔
2661

2662
        v = startswith(p, "tmp-dir=");
79✔
2663
        if (v) {
79✔
2664
                n = strcspn(v, " ");
79✔
2665
                tmp_dir = strndup(v, n);
79✔
2666
                if (!tmp_dir)
79✔
2667
                        return log_oom();
×
2668
                if (v[n] != ' ')
79✔
2669
                        goto finalize;
×
2670
                p = v + n + 1;
79✔
2671
        }
2672

2673
        v = startswith(p, "var-tmp-dir=");
79✔
2674
        if (v) {
79✔
2675
                n = strcspn(v, " ");
79✔
2676
                var_tmp_dir = strndup(v, n);
79✔
2677
                if (!var_tmp_dir)
79✔
2678
                        return log_oom();
×
2679
                if (v[n] != ' ')
79✔
2680
                        goto finalize;
78✔
2681
                p = v + n + 1;
1✔
2682
        }
2683

2684
        v = startswith(p, "userns-socket-0=");
1✔
2685
        if (v) {
1✔
2686
                char *buf;
×
2687

2688
                n = strcspn(v, " ");
×
2689
                buf = strndupa_safe(v, n);
×
2690

2691
                userns_fdpair[0] = deserialize_fd(fds, buf);
×
2692
                if (userns_fdpair[0] < 0)
×
2693
                        return userns_fdpair[0];
2694
                if (v[n] != ' ')
×
2695
                        goto finalize;
×
2696
                p = v + n + 1;
×
2697
        }
2698

2699
        v = startswith(p, "userns-socket-1=");
1✔
2700
        if (v) {
1✔
2701
                char *buf;
×
2702

2703
                n = strcspn(v, " ");
×
2704
                buf = strndupa_safe(v, n);
×
2705

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

2714
        v = startswith(p, "netns-socket-0=");
1✔
2715
        if (v) {
1✔
2716
                char *buf;
1✔
2717

2718
                n = strcspn(v, " ");
1✔
2719
                buf = strndupa_safe(v, n);
1✔
2720

2721
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2722
                if (netns_fdpair[0] < 0)
1✔
2723
                        return netns_fdpair[0];
2724
                if (v[n] != ' ')
1✔
2725
                        goto finalize;
×
2726
                p = v + n + 1;
1✔
2727
        }
2728

2729
        v = startswith(p, "netns-socket-1=");
1✔
2730
        if (v) {
1✔
2731
                char *buf;
1✔
2732

2733
                n = strcspn(v, " ");
1✔
2734
                buf = strndupa_safe(v, n);
1✔
2735

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

2744
        v = startswith(p, "ipcns-socket-0=");
×
2745
        if (v) {
×
2746
                char *buf;
×
2747

2748
                n = strcspn(v, " ");
×
2749
                buf = strndupa_safe(v, n);
×
2750

2751
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2752
                if (ipcns_fdpair[0] < 0)
×
2753
                        return ipcns_fdpair[0];
2754
                if (v[n] != ' ')
×
2755
                        goto finalize;
×
2756
                p = v + n + 1;
×
2757
        }
2758

2759
        v = startswith(p, "ipcns-socket-1=");
×
2760
        if (v) {
×
2761
                char *buf;
×
2762

2763
                n = strcspn(v, " ");
×
2764
                buf = strndupa_safe(v, n);
×
2765

2766
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2767
                if (ipcns_fdpair[1] < 0)
×
2768
                        return ipcns_fdpair[1];
2769
        }
2770

2771
finalize:
×
2772
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
79✔
2773
        if (r < 0)
79✔
2774
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2775
        return 0;
2776
}
2777

2778
void exec_shared_runtime_vacuum(Manager *m) {
1,510✔
2779
        ExecSharedRuntime *rt;
1,510✔
2780

2781
        assert(m);
1,510✔
2782

2783
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2784

2785
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,589✔
2786
                if (rt->n_ref > 0)
79✔
2787
                        continue;
79✔
2788

2789
                (void) exec_shared_runtime_free(rt);
×
2790
        }
2791
}
1,510✔
2792

2793
int exec_runtime_make(
5,678✔
2794
                const Unit *unit,
2795
                const ExecContext *context,
2796
                ExecSharedRuntime *shared,
2797
                DynamicCreds *creds,
2798
                ExecRuntime **ret) {
2799
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
5,678✔
2800
        _cleanup_free_ char *ephemeral = NULL;
5,678✔
2801
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
5,678✔
2802
        int r;
5,678✔
2803

2804
        assert(unit);
5,678✔
2805
        assert(context);
5,678✔
2806
        assert(ret);
5,678✔
2807

2808
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
5,678✔
2809
                *ret = NULL;
5,524✔
2810
                return 0;
5,524✔
2811
        }
2812

2813
        if (exec_needs_ephemeral(context)) {
154✔
2814
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2815
                if (r < 0)
×
2816
                        return r;
2817

2818
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2819
                if (r < 0)
×
2820
                        return r;
2821

2822
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2823
                        return -errno;
×
2824
        }
2825

2826
        rt = new(ExecRuntime, 1);
154✔
2827
        if (!rt)
154✔
2828
                return -ENOMEM;
2829

2830
        *rt = (ExecRuntime) {
154✔
2831
                .shared = shared,
2832
                .dynamic_creds = creds,
2833
                .ephemeral_copy = TAKE_PTR(ephemeral),
154✔
2834
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
154✔
2835
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
154✔
2836
        };
2837

2838
        *ret = TAKE_PTR(rt);
154✔
2839
        return 1;
154✔
2840
}
2841

2842
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
49,042✔
2843
        if (!rt)
49,042✔
2844
                return NULL;
2845

2846
        exec_shared_runtime_unref(rt->shared);
154✔
2847
        dynamic_creds_unref(rt->dynamic_creds);
154✔
2848

2849
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
154✔
2850

2851
        safe_close_pair(rt->ephemeral_storage_socket);
154✔
2852
        return mfree(rt);
154✔
2853
}
2854

2855
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,928✔
2856
        if (!rt)
5,928✔
2857
                return NULL;
2858

2859
        rt->shared = exec_shared_runtime_destroy(rt->shared);
54✔
2860
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
54✔
2861
        return exec_runtime_free(rt);
54✔
2862
}
2863

2864
void exec_runtime_clear(ExecRuntime *rt) {
32✔
2865
        if (!rt)
32✔
2866
                return;
2867

2868
        safe_close_pair(rt->ephemeral_storage_socket);
32✔
2869
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
32✔
2870
}
2871

2872
void exec_params_shallow_clear(ExecParameters *p) {
2,295✔
2873
        if (!p)
2,295✔
2874
                return;
2875

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

2879
        p->environment = strv_free(p->environment);
2,295✔
2880
        p->fd_names = strv_free(p->fd_names);
2,295✔
2881
        p->files_env = strv_free(p->files_env);
2,295✔
2882
        p->fds = mfree(p->fds);
2,295✔
2883
        p->exec_fd = safe_close(p->exec_fd);
2,295✔
2884
        p->user_lookup_fd = -EBADF;
2,295✔
2885
        p->bpf_restrict_fs_map_fd = -EBADF;
2,295✔
2886
        p->unit_id = mfree(p->unit_id);
2,295✔
2887
        p->invocation_id = SD_ID128_NULL;
2,295✔
2888
        p->invocation_id_string[0] = '\0';
2,295✔
2889
        p->confirm_spawn = mfree(p->confirm_spawn);
2,295✔
2890
}
2891

2892
void exec_params_deep_clear(ExecParameters *p) {
32✔
2893
        if (!p)
32✔
2894
                return;
2895

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

2900
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
32✔
2901

2902
        p->cgroup_path = mfree(p->cgroup_path);
32✔
2903

2904
        if (p->prefix) {
32✔
2905
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
32✔
2906
                p->prefix = mfree(p->prefix);
32✔
2907
        }
2908

2909
        p->received_credentials_directory = mfree(p->received_credentials_directory);
32✔
2910
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
32✔
2911

2912
        if (p->idle_pipe) {
32✔
2913
                close_many_and_free(p->idle_pipe, 4);
×
2914
                p->idle_pipe = NULL;
×
2915
        }
2916

2917
        p->stdin_fd = safe_close(p->stdin_fd);
32✔
2918
        p->stdout_fd = safe_close(p->stdout_fd);
32✔
2919
        p->stderr_fd = safe_close(p->stderr_fd);
32✔
2920
        p->root_directory_fd = safe_close(p->root_directory_fd);
32✔
2921

2922
        p->notify_socket = mfree(p->notify_socket);
32✔
2923

2924
        open_file_free_many(&p->open_files);
32✔
2925

2926
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
32✔
2927

2928
        exec_params_shallow_clear(p);
32✔
2929
}
2930

2931
void exec_directory_done(ExecDirectory *d) {
245,100✔
2932
        if (!d)
245,100✔
2933
                return;
2934

2935
        FOREACH_ARRAY(i, d->items, d->n_items) {
246,637✔
2936
                free(i->path);
1,537✔
2937
                strv_free(i->symlinks);
1,537✔
2938
        }
2939

2940
        d->items = mfree(d->items);
245,100✔
2941
        d->n_items = 0;
245,100✔
2942
        d->mode = 0755;
245,100✔
2943
}
2944

2945
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,215✔
2946
        assert(d);
5,215✔
2947
        assert(path);
5,215✔
2948

2949
        FOREACH_ARRAY(i, d->items, d->n_items)
7,579✔
2950
                if (path_equal(i->path, path))
2,379✔
2951
                        return i;
2952

2953
        return NULL;
2954
}
2955

2956
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,215✔
2957
        _cleanup_strv_free_ char **s = NULL;
×
2958
        _cleanup_free_ char *p = NULL;
5,215✔
2959
        ExecDirectoryItem *existing;
5,215✔
2960
        int r;
5,215✔
2961

2962
        assert(d);
5,215✔
2963
        assert(path);
5,215✔
2964

2965
        existing = exec_directory_find(d, path);
5,215✔
2966
        if (existing) {
5,215✔
2967
                r = strv_extend(&existing->symlinks, symlink);
15✔
2968
                if (r < 0)
15✔
2969
                        return r;
2970

2971
                existing->flags |= flags;
15✔
2972

2973
                return 0; /* existing item is updated */
15✔
2974
        }
2975

2976
        p = strdup(path);
5,200✔
2977
        if (!p)
5,200✔
2978
                return -ENOMEM;
2979

2980
        if (symlink) {
5,200✔
2981
                s = strv_new(symlink);
6✔
2982
                if (!s)
6✔
2983
                        return -ENOMEM;
2984
        }
2985

2986
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,200✔
2987
                return -ENOMEM;
2988

2989
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,200✔
2990
                .path = TAKE_PTR(p),
5,200✔
2991
                .symlinks = TAKE_PTR(s),
5,200✔
2992
                .flags = flags,
2993
        };
2994

2995
        return 1; /* new item is added */
5,200✔
2996
}
2997

2998
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
883✔
2999
        assert(a);
883✔
3000
        assert(b);
883✔
3001

3002
        return path_compare(a->path, b->path);
883✔
3003
}
3004

3005
void exec_directory_sort(ExecDirectory *d) {
135,516✔
3006
        assert(d);
135,516✔
3007

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

3013
        if (d->n_items <= 1)
135,516✔
3014
                return;
3015

3016
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
159✔
3017

3018
        for (size_t i = 1; i < d->n_items; i++)
690✔
3019
                for (size_t j = 0; j < i; j++)
1,766✔
3020
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,250✔
3021
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3022
                                break;
15✔
3023
                        }
3024
}
3025

3026
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
3027
        ExecDirectoryType t;
×
3028

3029
        assert(s);
×
3030

3031
        if (streq(s, "all"))
×
3032
                return EXEC_CLEAN_ALL;
3033
        if (streq(s, "fdstore"))
×
3034
                return EXEC_CLEAN_FDSTORE;
3035

3036
        t = exec_resource_type_from_string(s);
×
3037
        if (t < 0)
×
3038
                return (ExecCleanMask) t;
3039

3040
        return 1U << t;
×
3041
}
3042

3043
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
3044
        [EXEC_INPUT_NULL]      = "null",
3045
        [EXEC_INPUT_TTY]       = "tty",
3046
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
3047
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
3048
        [EXEC_INPUT_SOCKET]    = "socket",
3049
        [EXEC_INPUT_NAMED_FD]  = "fd",
3050
        [EXEC_INPUT_DATA]      = "data",
3051
        [EXEC_INPUT_FILE]      = "file",
3052
};
3053

3054
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,603✔
3055

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

3071
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,417✔
3072

3073
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3074
        [EXEC_UTMP_INIT]  = "init",
3075
        [EXEC_UTMP_LOGIN] = "login",
3076
        [EXEC_UTMP_USER]  = "user",
3077
};
3078

3079
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,881✔
3080

3081
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3082
        [EXEC_PRESERVE_NO]      = "no",
3083
        [EXEC_PRESERVE_YES]     = "yes",
3084
        [EXEC_PRESERVE_RESTART] = "restart",
3085
};
3086

3087
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,922✔
3088

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

3098
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3099

3100
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3101
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3102
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3103
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3104
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3105
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3106
};
3107

3108
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3109

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

3121
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
318✔
3122

3123
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3124
        [EXEC_KEYRING_INHERIT] = "inherit",
3125
        [EXEC_KEYRING_PRIVATE] = "private",
3126
        [EXEC_KEYRING_SHARED]  = "shared",
3127
};
3128

3129
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
16,219✔
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