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

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

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

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

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <linux/prctl.h>
6
#include <poll.h>
7
#include <sys/file.h>
8
#include <sys/mman.h>
9
#include <sys/personality.h>
10
#include <sys/prctl.h>
11
#include <sys/shm.h>
12
#include <sys/types.h>
13
#include <sys/un.h>
14
#include <unistd.h>
15
#include <utmpx.h>
16

17
#include "sd-messages.h"
18

19
#include "af-list.h"
20
#include "alloc-util.h"
21
#include "async.h"
22
#include "bitfield.h"
23
#include "cap-list.h"
24
#include "capability-util.h"
25
#include "cgroup-setup.h"
26
#include "constants.h"
27
#include "cpu-set-util.h"
28
#include "dynamic-user.h"
29
#include "env-file.h"
30
#include "env-util.h"
31
#include "errno-list.h"
32
#include "escape.h"
33
#include "exec-credential.h"
34
#include "execute.h"
35
#include "execute-serialize.h"
36
#include "exit-status.h"
37
#include "fd-util.h"
38
#include "fileio.h"
39
#include "format-util.h"
40
#include "glob-util.h"
41
#include "hexdecoct.h"
42
#include "io-util.h"
43
#include "ioprio-util.h"
44
#include "lock-util.h"
45
#include "log.h"
46
#include "macro.h"
47
#include "manager.h"
48
#include "manager-dump.h"
49
#include "memory-util.h"
50
#include "missing_fs.h"
51
#include "mkdir-label.h"
52
#include "namespace.h"
53
#include "osc-context.h"
54
#include "parse-util.h"
55
#include "path-util.h"
56
#include "process-util.h"
57
#include "rlimit-util.h"
58
#include "rm-rf.h"
59
#include "seccomp-util.h"
60
#include "securebits-util.h"
61
#include "selinux-util.h"
62
#include "serialize.h"
63
#include "sort-util.h"
64
#include "special.h"
65
#include "stat-util.h"
66
#include "string-table.h"
67
#include "string-util.h"
68
#include "strv.h"
69
#include "syslog-util.h"
70
#include "terminal-util.h"
71
#include "tmpfile-util.h"
72
#include "umask-util.h"
73
#include "unit-serialize.h"
74
#include "user-util.h"
75
#include "utmp-wtmp.h"
76

77
static bool is_terminal_input(ExecInput i) {
33,860✔
78
        return IN_SET(i,
33,860✔
79
                      EXEC_INPUT_TTY,
80
                      EXEC_INPUT_TTY_FORCE,
81
                      EXEC_INPUT_TTY_FAIL);
82
}
83

84
static bool is_terminal_output(ExecOutput o) {
66,661✔
85
        return IN_SET(o,
66,661✔
86
                      EXEC_OUTPUT_TTY,
87
                      EXEC_OUTPUT_KMSG_AND_CONSOLE,
88
                      EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
89
}
90

91
const char* exec_context_tty_path(const ExecContext *context) {
14,662✔
92
        assert(context);
14,662✔
93

94
        if (context->stdio_as_fds)
14,662✔
95
                return NULL;
96

97
        if (context->tty_path)
14,009✔
98
                return context->tty_path;
679✔
99

100
        return "/dev/console";
101
}
102

103
int exec_context_apply_tty_size(
1,805✔
104
                const ExecContext *context,
105
                int input_fd,
106
                int output_fd,
107
                const char *tty_path) {
108

109
        unsigned rows, cols;
1,805✔
110
        int r;
1,805✔
111

112
        assert(context);
1,805✔
113
        assert(input_fd >= 0);
1,805✔
114
        assert(output_fd >= 0);
1,805✔
115

116
        if (!isatty_safe(output_fd))
1,805✔
117
                return 0;
1,805✔
118

119
        if (!tty_path)
1,068✔
120
                tty_path = exec_context_tty_path(context);
411✔
121

122
        /* Preferably use explicitly configured data */
123
        rows = context->tty_rows;
1,068✔
124
        cols = context->tty_cols;
1,068✔
125

126
        /* Fill in data from kernel command line if anything is unspecified */
127
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,068✔
128
                (void) proc_cmdline_tty_size(
1,036✔
129
                                tty_path,
130
                                rows == UINT_MAX ? &rows : NULL,
131
                                cols == UINT_MAX ? &cols : NULL);
132

133
        /* If we got nothing so far and we are talking to a physical device, then let's query dimensions from
134
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
135
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
136
         * use those during initialization we need to skip it. */
137
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,275✔
138
            exec_context_shall_ansi_seq_reset(context) &&
386✔
139
            isatty_safe(input_fd)) {
179✔
140
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
179✔
141
                if (r < 0)
179✔
142
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
179✔
143
        }
144

145
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
1,068✔
146
}
147

148
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p, sd_id128_t invocation_id) {
13,008✔
149
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
26,016✔
150
        int fd, r;
13,008✔
151

152
        assert(context);
13,008✔
153

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

159
        const char *path = exec_context_tty_path(context);
13,008✔
160

161
        if (p && p->stdout_fd >= 0 && isatty_safe(p->stdout_fd))
13,008✔
162
                fd = p->stdout_fd;
16✔
163
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
12,992✔
164
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
12,034✔
165
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
657✔
166
                if (fd < 0)
657✔
167
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
×
168
        } else
169
                return;   /* nothing to do */
170

171
        /* Take a synchronization lock for the duration of the setup that we do here.
172
         * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
173
         * that will be closed automatically, and operate on it for convenience. */
174
        lock_fd = lock_dev_console();
673✔
175
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
673✔
176
                log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without lock: %m");
×
177
        else if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd))
673✔
178
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
179
        else if (lock_fd < 0)
673✔
180
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
181

182
        if (context->tty_reset)
673✔
183
                (void) terminal_reset_defensive(
170✔
184
                                fd,
185
                                TERMINAL_RESET_SWITCH_TO_TEXT |
186
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
167✔
187

188
        r = exec_context_apply_tty_size(context, fd, fd, path);
673✔
189
        if (r < 0)
673✔
190
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
191

192
        if (!sd_id128_is_null(invocation_id)) {
1,297✔
193
                sd_id128_t context_id;
49✔
194

195
                r = osc_context_id_from_invocation_id(invocation_id, &context_id);
49✔
196
                if (r < 0)
49✔
197
                        log_debug_errno(r, "Failed to derive context ID from invocation ID, ignoring: %m");
49✔
198
                else {
199
                        _cleanup_free_ char *seq = NULL;
49✔
200

201
                        r = osc_context_close(context_id, &seq);
49✔
202
                        if (r < 0)
49✔
203
                                log_debug_errno(r, "Failed to acquire OSC close sequence, ignoring: %m");
×
204
                        else
205
                                (void) loop_write(fd, seq, SIZE_MAX);
49✔
206
                }
207
        }
208

209
        if (context->tty_vhangup)
673✔
210
                (void) terminal_vhangup_fd(fd);
162✔
211

212
        /* We don't need the fd anymore now, and it potentially points to a hungup TTY anyway, let's close it
213
         * hence. */
214
        _fd = safe_close(_fd);
673✔
215

216
        if (context->tty_vt_disallocate && path)
673✔
217
                (void) vt_disallocate(path);
89✔
218
}
219

220
bool exec_needs_network_namespace(const ExecContext *context) {
56,115✔
221
        assert(context);
56,115✔
222

223
        return context->private_network || context->network_namespace_path;
56,115✔
224
}
225

226
static bool exec_needs_ephemeral(const ExecContext *context) {
6,184✔
227
        return (context->root_image || context->root_directory) && context->root_ephemeral;
6,184✔
228
}
229

230
bool exec_needs_ipc_namespace(const ExecContext *context) {
52,044✔
231
        assert(context);
52,044✔
232

233
        return context->private_ipc || context->ipc_namespace_path;
52,044✔
234
}
235

236
static bool needs_cgroup_namespace(ProtectControlGroups i) {
90,790✔
237
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
90,790✔
238
}
239

240
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
60,607✔
241
        assert(context);
60,607✔
242

243
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
244
         * use cgroup namespace, we ignore the setting and do not unshare the namespace.
245
         * ProtectControlGroups=private and strict get downgraded to no and yes respectively. This ensures
246
         * that strict always gets a read-only mount of /sys/fs/cgroup/. */
247
        if (needs_cgroup_namespace(context->protect_control_groups) && !namespace_type_supported(NAMESPACE_CGROUP)) {
60,607✔
248
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
249
                        return PROTECT_CONTROL_GROUPS_NO;
250
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
251
                        return PROTECT_CONTROL_GROUPS_YES;
252
        }
253
        return context->protect_control_groups;
60,607✔
254
}
255

256
bool exec_needs_cgroup_namespace(const ExecContext *context) {
30,183✔
257
        assert(context);
30,183✔
258

259
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
30,183✔
260
}
261

262
bool exec_needs_cgroup_mount(const ExecContext *context) {
26,406✔
263
        assert(context);
26,406✔
264

265
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
26,406✔
266
}
267

268
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,009✔
269
        assert(context);
2,009✔
270

271
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,009✔
272
}
273

274
bool exec_needs_pid_namespace(const ExecContext *context) {
72,829✔
275
        assert(context);
72,829✔
276

277
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
72,829✔
278
}
279

280
bool exec_needs_mount_namespace(
31,638✔
281
                const ExecContext *context,
282
                const ExecParameters *params,
283
                const ExecRuntime *runtime) {
284

285
        assert(context);
31,638✔
286

287
        if (context->root_image)
31,638✔
288
                return true;
289

290
        if (!strv_isempty(context->read_write_paths) ||
31,618✔
291
            !strv_isempty(context->read_only_paths) ||
29,537✔
292
            !strv_isempty(context->inaccessible_paths) ||
29,524✔
293
            !strv_isempty(context->exec_paths) ||
29,505✔
294
            !strv_isempty(context->no_exec_paths))
29,505✔
295
                return true;
296

297
        if (context->n_bind_mounts > 0)
29,505✔
298
                return true;
299

300
        if (context->n_temporary_filesystems > 0)
29,458✔
301
                return true;
302

303
        if (context->n_mount_images > 0)
29,299✔
304
                return true;
305

306
        if (context->n_extension_images > 0)
29,276✔
307
                return true;
308

309
        if (!strv_isempty(context->extension_directories))
29,265✔
310
                return true;
311

312
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
29,260✔
313
                return true;
314

315
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
29,260✔
316
                return true;
317

318
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
28,249✔
319
                return true;
320

321
        if (context->private_devices ||
27,769✔
322
            context->private_mounts > 0 ||
27,029✔
323
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
26,578✔
324
            context->protect_system != PROTECT_SYSTEM_NO ||
26,555✔
325
            context->protect_home != PROTECT_HOME_NO ||
326
            context->protect_kernel_tunables ||
327
            context->protect_kernel_modules ||
26,555✔
328
            context->protect_kernel_logs ||
24,881✔
329
            exec_needs_cgroup_mount(context) ||
24,881✔
330
            context->protect_proc != PROTECT_PROC_DEFAULT ||
24,863✔
331
            context->proc_subset != PROC_SUBSET_ALL ||
24,806✔
332
            exec_needs_ipc_namespace(context) ||
49,612✔
333
            exec_needs_pid_namespace(context))
24,806✔
334
                return true;
3,010✔
335

336
        if (context->root_directory) {
24,759✔
337
                if (exec_context_get_effective_mount_apivfs(context))
5✔
338
                        return true;
339

340
                for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
341
                        if (params && !params->prefix[t])
×
342
                                continue;
×
343

344
                        if (context->directories[t].n_items > 0)
×
345
                                return true;
346
                }
347
        }
348

349
        if (context->dynamic_user &&
24,754✔
350
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
351
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
352
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
353
                return true;
354

355
        if (exec_context_get_effective_bind_log_sockets(context))
24,754✔
356
                return true;
357

358
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
148,180✔
359
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
128,308✔
360
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
4,876✔
361
                                return true;
362

363
        return false;
364
}
365

366
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,895✔
367
        assert(context);
3,895✔
368
        assert(params);
3,895✔
369

370
        if (!params->notify_socket)
3,895✔
371
                return NULL;
372

373
        if (!needs_sandboxing)
3,244✔
374
                return NULL;
375

376
        if (!context->root_directory && !context->root_image)
3,244✔
377
                return NULL;
378

379
        if (!exec_context_get_effective_mount_apivfs(context))
×
380
                return NULL;
381

382
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
383
                return NULL;
×
384

385
        return "/run/host/notify";
386
}
387

388
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,815✔
389
        assert(context);
11,815✔
390

391
        if (!context->dynamic_user)
11,815✔
392
                return false;
393

394
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
395
                return false;
396

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

400
        return true;
401
}
402

403
int exec_params_get_cgroup_path(
14,360✔
404
                const ExecParameters *params,
405
                const CGroupContext *c,
406
                char **ret) {
407

408
        const char *subgroup = NULL;
14,360✔
409
        char *p;
14,360✔
410

411
        assert(params);
14,360✔
412
        assert(ret);
14,360✔
413

414
        if (!params->cgroup_path)
14,360✔
415
                return -EINVAL;
416

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

426
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
14,360✔
427
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
696✔
428
                        subgroup = ".control";
429
                else
430
                        subgroup = c->delegate_subgroup;
662✔
431
        }
432

433
        if (subgroup)
662✔
434
                p = path_join(params->cgroup_path, subgroup);
696✔
435
        else
436
                p = strdup(params->cgroup_path);
13,664✔
437
        if (!p)
14,360✔
438
                return -ENOMEM;
439

440
        *ret = p;
14,360✔
441
        return !!subgroup;
14,360✔
442
}
443

444
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
1,260✔
445
        assert(c);
1,260✔
446

447
        return c->cpu_affinity_from_numa;
1,260✔
448
}
449

450
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,199✔
451
        assert(unit);
2,199✔
452
        assert(msg);
2,199✔
453
        assert(executable);
2,199✔
454

455
        if (!DEBUG_LOGGING)
2,199✔
456
                return;
2,199✔
457

458
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,398✔
459

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

466
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
467

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

477
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,199✔
478
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
479
        _cleanup_fclose_ FILE *f = NULL;
2,199✔
480
        int r;
2,199✔
481

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

494
        LOG_CONTEXT_PUSH_UNIT(unit);
4,398✔
495

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

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

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

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

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

525
        r = open_serialization_file("sd-executor-state", &f);
2,199✔
526
        if (r < 0)
2,199✔
527
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
528

529
        fdset = fdset_new();
2,199✔
530
        if (!fdset)
2,199✔
531
                return log_oom();
×
532

533
        r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
2,199✔
534
        if (r < 0)
2,199✔
535
                return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
×
536

537
        r = finish_serialization_file(f);
2,199✔
538
        if (r < 0)
2,199✔
539
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
540

541
        r = fd_cloexec(fileno(f), false);
2,199✔
542
        if (r < 0)
2,199✔
543
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
544

545
        r = fdset_cloexec(fdset, false);
2,199✔
546
        if (r < 0)
2,199✔
547
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
548

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

556
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,199✔
557
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,199✔
558

559
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,199✔
560
        dual_timestamp start_timestamp;
2,199✔
561

562
        /* Restore the original ambient capability set the manager was started with to pass it to
563
         * sd-executor. */
564
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
2,199✔
565
        if (r < 0)
2,199✔
566
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
567

568
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
569
         * handoff timestamp. */
570
        dual_timestamp_now(&start_timestamp);
2,199✔
571

572
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
573
        r = posix_spawn_wrapper(
2,199✔
574
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,199✔
575
                        STRV_MAKE(unit->manager->executor_path,
2,199✔
576
                                  "--deserialize", serialization_fd_number,
577
                                  "--log-level", max_log_levels,
578
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
579
                        environ,
580
                        subcgroup_path,
581
                        &pidref);
582

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

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

600
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,199✔
601
                       command->path, pidref.pid, r > 0 ? "via" : "without");
602

603
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,199✔
604

605
        *ret = TAKE_PIDREF(pidref);
2,199✔
606
        return 0;
2,199✔
607
}
608

609
void exec_context_init(ExecContext *c) {
57,637✔
610
        assert(c);
57,637✔
611

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

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

641
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
345,822✔
642
                d->mode = 0755;
288,185✔
643

644
        numa_policy_reset(&c->numa_policy);
57,637✔
645

646
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
57,637✔
647
}
57,637✔
648

649
void exec_context_done(ExecContext *c) {
46,166✔
650
        assert(c);
46,166✔
651

652
        c->environment = strv_free(c->environment);
46,166✔
653
        c->environment_files = strv_free(c->environment_files);
46,166✔
654
        c->pass_environment = strv_free(c->pass_environment);
46,166✔
655
        c->unset_environment = strv_free(c->unset_environment);
46,166✔
656

657
        rlimit_free_all(c->rlimit);
46,166✔
658

659
        for (size_t l = 0; l < 3; l++) {
184,664✔
660
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
138,498✔
661
                c->stdio_file[l] = mfree(c->stdio_file[l]);
138,498✔
662
        }
663

664
        c->working_directory = mfree(c->working_directory);
46,166✔
665
        c->root_directory = mfree(c->root_directory);
46,166✔
666
        c->root_image = mfree(c->root_image);
46,166✔
667
        c->root_image_options = mount_options_free_all(c->root_image_options);
46,166✔
668
        c->root_hash = mfree(c->root_hash);
46,166✔
669
        c->root_hash_size = 0;
46,166✔
670
        c->root_hash_path = mfree(c->root_hash_path);
46,166✔
671
        c->root_hash_sig = mfree(c->root_hash_sig);
46,166✔
672
        c->root_hash_sig_size = 0;
46,166✔
673
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
46,166✔
674
        c->root_verity = mfree(c->root_verity);
46,166✔
675
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
46,166✔
676
        c->extension_directories = strv_free(c->extension_directories);
46,166✔
677
        c->tty_path = mfree(c->tty_path);
46,166✔
678
        c->syslog_identifier = mfree(c->syslog_identifier);
46,166✔
679
        c->user = mfree(c->user);
46,166✔
680
        c->group = mfree(c->group);
46,166✔
681

682
        c->supplementary_groups = strv_free(c->supplementary_groups);
46,166✔
683

684
        c->pam_name = mfree(c->pam_name);
46,166✔
685

686
        c->read_only_paths = strv_free(c->read_only_paths);
46,166✔
687
        c->read_write_paths = strv_free(c->read_write_paths);
46,166✔
688
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
46,166✔
689
        c->exec_paths = strv_free(c->exec_paths);
46,166✔
690
        c->no_exec_paths = strv_free(c->no_exec_paths);
46,166✔
691
        c->exec_search_path = strv_free(c->exec_search_path);
46,166✔
692

693
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
46,166✔
694
        c->bind_mounts = NULL;
46,166✔
695
        c->n_bind_mounts = 0;
46,166✔
696
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
46,166✔
697
        c->temporary_filesystems = NULL;
46,166✔
698
        c->n_temporary_filesystems = 0;
46,166✔
699
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
46,166✔
700

701
        cpu_set_reset(&c->cpu_set);
46,166✔
702
        numa_policy_reset(&c->numa_policy);
46,166✔
703

704
        c->utmp_id = mfree(c->utmp_id);
46,166✔
705
        c->selinux_context = mfree(c->selinux_context);
46,166✔
706
        c->apparmor_profile = mfree(c->apparmor_profile);
46,166✔
707
        c->smack_process_label = mfree(c->smack_process_label);
46,166✔
708

709
        c->restrict_filesystems = set_free(c->restrict_filesystems);
46,166✔
710

711
        c->syscall_filter = hashmap_free(c->syscall_filter);
46,166✔
712
        c->syscall_archs = set_free(c->syscall_archs);
46,166✔
713
        c->syscall_log = hashmap_free(c->syscall_log);
46,166✔
714
        c->address_families = set_free(c->address_families);
46,166✔
715

716
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
276,996✔
717
                exec_directory_done(d);
230,830✔
718

719
        c->log_level_max = -1;
46,166✔
720

721
        exec_context_free_log_extra_fields(c);
46,166✔
722
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
46,166✔
723
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
46,166✔
724

725
        c->log_ratelimit = (RateLimit) {};
46,166✔
726

727
        c->stdin_data = mfree(c->stdin_data);
46,166✔
728
        c->stdin_data_size = 0;
46,166✔
729

730
        c->network_namespace_path = mfree(c->network_namespace_path);
46,166✔
731
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
46,166✔
732

733
        c->log_namespace = mfree(c->log_namespace);
46,166✔
734

735
        c->load_credentials = hashmap_free(c->load_credentials);
46,166✔
736
        c->set_credentials = hashmap_free(c->set_credentials);
46,166✔
737
        c->import_credentials = ordered_set_free(c->import_credentials);
46,166✔
738

739
        c->root_image_policy = image_policy_free(c->root_image_policy);
46,166✔
740
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
46,166✔
741
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
46,166✔
742

743
        c->private_hostname = mfree(c->private_hostname);
46,166✔
744
}
46,166✔
745

746
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
4,911✔
747
        assert(c);
4,911✔
748

749
        if (!runtime_prefix)
4,911✔
750
                return 0;
751

752
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
4,924✔
753
                _cleanup_free_ char *p = NULL;
13✔
754

755
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
13✔
756
                        p = path_join(runtime_prefix, "private", i->path);
×
757
                else
758
                        p = path_join(runtime_prefix, i->path);
13✔
759
                if (!p)
13✔
760
                        return -ENOMEM;
761

762
                /* We execute this synchronously, since we need to be sure this is gone when we start the
763
                 * service next. */
764
                (void) rm_rf(p, REMOVE_ROOT);
13✔
765

766
                STRV_FOREACH(symlink, i->symlinks) {
13✔
767
                        _cleanup_free_ char *symlink_abs = NULL;
×
768

769
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
770
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
771
                        else
772
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
773
                        if (!symlink_abs)
×
774
                                return -ENOMEM;
×
775

776
                        (void) unlink(symlink_abs);
×
777
                }
778
        }
779

780
        return 0;
781
}
782

783
int exec_context_destroy_mount_ns_dir(Unit *u) {
10,881✔
784
        _cleanup_free_ char *p = NULL;
10,881✔
785

786
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
10,881✔
787
                return 0;
788

789
        p = path_join("/run/systemd/propagate/", u->id);
2,011✔
790
        if (!p)
2,011✔
791
                return -ENOMEM;
792

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

797
        return 0;
798
}
799

800
void exec_command_done(ExecCommand *c) {
98,557✔
801
        assert(c);
98,557✔
802

803
        c->path = mfree(c->path);
98,557✔
804
        c->argv = strv_free(c->argv);
98,557✔
805
}
98,557✔
806

807
void exec_command_done_array(ExecCommand *c, size_t n) {
27,357✔
808
        FOREACH_ARRAY(i, c, n)
109,426✔
809
                exec_command_done(i);
82,069✔
810
}
27,357✔
811

812
ExecCommand* exec_command_free(ExecCommand *c) {
16,460✔
813
        if (!c)
16,460✔
814
                return NULL;
815

816
        exec_command_done(c);
16,460✔
817
        return mfree(c);
16,460✔
818
}
819

820
ExecCommand* exec_command_free_list(ExecCommand *c) {
121,218✔
821
        ExecCommand *i;
121,218✔
822

823
        while ((i = LIST_POP(command, c)))
137,678✔
824
                exec_command_free(i);
16,460✔
825

826
        return NULL;
121,218✔
827
}
828

829
void exec_command_free_array(ExecCommand **c, size_t n) {
18,781✔
830
        FOREACH_ARRAY(i, c, n)
139,984✔
831
                *i = exec_command_free_list(*i);
121,203✔
832
}
18,781✔
833

834
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
6,877✔
835
        FOREACH_ARRAY(i, c, n)
27,507✔
836
                exec_status_reset(&i->exec_status);
20,630✔
837
}
6,877✔
838

839
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,187✔
840
        FOREACH_ARRAY(i, c, n)
28,962✔
841
                LIST_FOREACH(command, z, *i)
26,951✔
842
                        exec_status_reset(&z->exec_status);
2,176✔
843
}
4,187✔
844

845
typedef struct InvalidEnvInfo {
846
        const Unit *unit;
847
        const char *path;
848
} InvalidEnvInfo;
849

850
static void invalid_env(const char *p, void *userdata) {
×
851
        InvalidEnvInfo *info = userdata;
×
852

853
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
854
}
×
855

856
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
38,280✔
857
        assert(c);
38,280✔
858

859
        switch (fd_index) {
38,280✔
860

861
        case STDIN_FILENO:
12,760✔
862
                if (c->std_input != EXEC_INPUT_NAMED_FD)
12,760✔
863
                        return NULL;
864

865
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
866

867
        case STDOUT_FILENO:
12,760✔
868
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
12,760✔
869
                        return NULL;
870

871
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
872

873
        case STDERR_FILENO:
12,760✔
874
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
12,760✔
875
                        return NULL;
876

877
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
878

879
        default:
880
                return NULL;
881
        }
882
}
883

884
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,199✔
885
        _cleanup_strv_free_ char **v = NULL;
2,199✔
886
        int r;
2,199✔
887

888
        assert(c);
2,199✔
889
        assert(ret);
2,199✔
890

891
        STRV_FOREACH(i, c->environment_files) {
2,201✔
892
                _cleanup_globfree_ glob_t pglob = {};
2✔
893
                bool ignore = false;
2✔
894
                char *fn = *i;
2✔
895

896
                if (fn[0] == '-') {
2✔
897
                        ignore = true;
1✔
898
                        fn++;
1✔
899
                }
900

901
                if (!path_is_absolute(fn)) {
2✔
902
                        if (ignore)
×
903
                                continue;
×
904
                        return -EINVAL;
905
                }
906

907
                /* Filename supports globbing, take all matching files */
908
                r = safe_glob(fn, 0, &pglob);
2✔
909
                if (r < 0) {
2✔
910
                        if (ignore)
1✔
911
                                continue;
1✔
912
                        return r;
913
                }
914

915
                /* When we don't match anything, -ENOENT should be returned */
916
                assert(pglob.gl_pathc > 0);
1✔
917

918
                FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) {
2✔
919
                        _cleanup_strv_free_ char **p = NULL;
1✔
920

921
                        r = load_env_file(NULL, *path, &p);
1✔
922
                        if (r < 0) {
1✔
923
                                if (ignore)
×
924
                                        continue;
×
925
                                return r;
926
                        }
927

928
                        /* Log invalid environment variables with filename */
929
                        if (p) {
1✔
930
                                InvalidEnvInfo info = {
1✔
931
                                        .unit = unit,
932
                                        .path = *path,
1✔
933
                                };
934

935
                                p = strv_env_clean_with_callback(p, invalid_env, &info);
1✔
936
                        }
937

938
                        if (!v)
1✔
939
                                v = TAKE_PTR(p);
1✔
940
                        else {
941
                                char **m = strv_env_merge(v, p);
×
942
                                if (!m)
×
943
                                        return -ENOMEM;
×
944

945
                                strv_free_and_replace(v, m);
×
946
                        }
947
                }
948
        }
949

950
        *ret = TAKE_PTR(v);
2,199✔
951

952
        return 0;
2,199✔
953
}
954

955
static bool tty_may_match_dev_console(const char *tty) {
367✔
956
        _cleanup_free_ char *resolved = NULL;
367✔
957

958
        if (!tty)
367✔
959
                return true;
960

961
        tty = skip_dev_prefix(tty);
367✔
962

963
        /* trivial identity? */
964
        if (streq(tty, "console"))
367✔
965
                return true;
966

967
        if (resolve_dev_console(&resolved) < 0)
39✔
968
                return true; /* if we could not resolve, assume it may */
969

970
        /* "tty0" means the active VC, so it may be the same sometimes */
971
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
39✔
972
}
973

974
static bool exec_context_may_touch_tty(const ExecContext *ec) {
21,769✔
975
        assert(ec);
21,769✔
976

977
        return ec->tty_reset ||
21,769✔
978
                ec->tty_vhangup ||
21,769✔
979
                ec->tty_vt_disallocate ||
21,619✔
980
                is_terminal_input(ec->std_input) ||
21,619✔
981
                is_terminal_output(ec->std_output) ||
43,296✔
982
                is_terminal_output(ec->std_error);
21,353✔
983
}
984

985
bool exec_context_may_touch_console(const ExecContext *ec) {
20,260✔
986

987
        return exec_context_may_touch_tty(ec) &&
20,627✔
988
               tty_may_match_dev_console(exec_context_tty_path(ec));
367✔
989
}
990

991
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,506✔
992
        assert(c);
1,506✔
993

994
        /* Determines whether ANSI sequences shall be used during any terminal initialisation:
995
         *
996
         * 1. If the reset logic is enabled at all, this is an immediate no.
997
         *
998
         * 2. If $TERM is set to anything other than "dumb", it's a yes.
999
         */
1000

1001
        if (!c->tty_reset)
1,506✔
1002
                return false;
1003

1004
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
505✔
1005
}
1006

1007
static void strv_fprintf(FILE *f, char **l) {
×
1008
        assert(f);
×
1009

1010
        STRV_FOREACH(g, l)
×
1011
                fprintf(f, " %s", *g);
×
1012
}
×
1013

1014
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,792✔
1015
        assert(f);
1,792✔
1016
        assert(prefix);
1,792✔
1017
        assert(name);
1,792✔
1018

1019
        if (!strv_isempty(strv)) {
1,792✔
1020
                fprintf(f, "%s%s:", prefix, name);
×
1021
                strv_fprintf(f, strv);
×
1022
                fputs("\n", f);
×
1023
        }
1024
}
1,792✔
1025

1026
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1027
        assert(p);
×
1028
        assert(f);
×
1029

1030
        prefix = strempty(prefix);
×
1031

1032
        fprintf(f,
×
1033
                "%sRuntimeScope: %s\n"
1034
                "%sExecFlags: %u\n"
1035
                "%sSELinuxContextNetwork: %s\n"
1036
                "%sCgroupSupportedMask: %u\n"
1037
                "%sCgroupPath: %s\n"
1038
                "%sCrededentialsDirectory: %s\n"
1039
                "%sEncryptedCredentialsDirectory: %s\n"
1040
                "%sConfirmSpawn: %s\n"
1041
                "%sShallConfirmSpawn: %s\n"
1042
                "%sWatchdogUSec: " USEC_FMT "\n"
1043
                "%sNotifySocket: %s\n"
1044
                "%sDebugInvocation: %s\n"
1045
                "%sFallbackSmackProcessLabel: %s\n",
1046
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1047
                prefix, p->flags,
×
1048
                prefix, yes_no(p->selinux_context_net),
×
1049
                prefix, p->cgroup_supported,
×
1050
                prefix, p->cgroup_path,
×
1051
                prefix, strempty(p->received_credentials_directory),
×
1052
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1053
                prefix, strempty(p->confirm_spawn),
×
1054
                prefix, yes_no(p->shall_confirm_spawn),
×
1055
                prefix, p->watchdog_usec,
×
1056
                prefix, strempty(p->notify_socket),
×
1057
                prefix, yes_no(p->debug_invocation),
×
1058
                prefix, strempty(p->fallback_smack_process_label));
×
1059

1060
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1061
        strv_dump(f, prefix, "Environment", p->environment);
×
1062
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1063

1064
        LIST_FOREACH(open_files, file, p->open_files)
×
1065
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1066

1067
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1068
}
×
1069

1070
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
224✔
1071
        int r;
224✔
1072

1073
        assert(c);
224✔
1074
        assert(f);
224✔
1075

1076
        prefix = strempty(prefix);
224✔
1077

1078
        fprintf(f,
224✔
1079
                "%sUMask: %04o\n"
1080
                "%sWorkingDirectory: %s\n"
1081
                "%sRootDirectory: %s\n"
1082
                "%sRootEphemeral: %s\n"
1083
                "%sNonBlocking: %s\n"
1084
                "%sPrivateTmp: %s\n"
1085
                "%sPrivateDevices: %s\n"
1086
                "%sProtectKernelTunables: %s\n"
1087
                "%sProtectKernelModules: %s\n"
1088
                "%sProtectKernelLogs: %s\n"
1089
                "%sProtectClock: %s\n"
1090
                "%sProtectControlGroups: %s\n"
1091
                "%sPrivateNetwork: %s\n"
1092
                "%sPrivateUsers: %s\n"
1093
                "%sPrivatePIDs: %s\n"
1094
                "%sProtectHome: %s\n"
1095
                "%sProtectSystem: %s\n"
1096
                "%sMountAPIVFS: %s\n"
1097
                "%sBindLogSockets: %s\n"
1098
                "%sIgnoreSIGPIPE: %s\n"
1099
                "%sMemoryDenyWriteExecute: %s\n"
1100
                "%sRestrictRealtime: %s\n"
1101
                "%sRestrictSUIDSGID: %s\n"
1102
                "%sKeyringMode: %s\n"
1103
                "%sProtectHostname: %s%s%s\n"
1104
                "%sProtectProc: %s\n"
1105
                "%sProcSubset: %s\n",
1106
                prefix, c->umask,
224✔
1107
                prefix, empty_to_root(c->working_directory),
224✔
1108
                prefix, empty_to_root(c->root_directory),
224✔
1109
                prefix, yes_no(c->root_ephemeral),
224✔
1110
                prefix, yes_no(c->non_blocking),
224✔
1111
                prefix, private_tmp_to_string(c->private_tmp),
224✔
1112
                prefix, yes_no(c->private_devices),
224✔
1113
                prefix, yes_no(c->protect_kernel_tunables),
224✔
1114
                prefix, yes_no(c->protect_kernel_modules),
224✔
1115
                prefix, yes_no(c->protect_kernel_logs),
224✔
1116
                prefix, yes_no(c->protect_clock),
224✔
1117
                prefix, protect_control_groups_to_string(c->protect_control_groups),
224✔
1118
                prefix, yes_no(c->private_network),
224✔
1119
                prefix, private_users_to_string(c->private_users),
224✔
1120
                prefix, private_pids_to_string(c->private_pids),
224✔
1121
                prefix, protect_home_to_string(c->protect_home),
224✔
1122
                prefix, protect_system_to_string(c->protect_system),
224✔
1123
                prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
224✔
1124
                prefix, yes_no(exec_context_get_effective_bind_log_sockets(c)),
224✔
1125
                prefix, yes_no(c->ignore_sigpipe),
224✔
1126
                prefix, yes_no(c->memory_deny_write_execute),
224✔
1127
                prefix, yes_no(c->restrict_realtime),
224✔
1128
                prefix, yes_no(c->restrict_suid_sgid),
224✔
1129
                prefix, exec_keyring_mode_to_string(c->keyring_mode),
224✔
1130
                prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
448✔
1131
                prefix, protect_proc_to_string(c->protect_proc),
224✔
1132
                prefix, proc_subset_to_string(c->proc_subset));
224✔
1133

1134
        if (c->set_login_environment >= 0)
224✔
1135
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1136

1137
        if (c->root_image)
224✔
1138
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1139

1140
        if (c->root_image_options) {
224✔
1141
                fprintf(f, "%sRootImageOptions:", prefix);
×
1142
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1143
                        if (!isempty(o->options))
×
1144
                                fprintf(f, " %s:%s",
×
1145
                                        partition_designator_to_string(o->partition_designator),
1146
                                        o->options);
1147
                fprintf(f, "\n");
×
1148
        }
1149

1150
        if (c->root_hash) {
224✔
1151
                _cleanup_free_ char *encoded = NULL;
×
1152
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1153
                if (encoded)
×
1154
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1155
        }
1156

1157
        if (c->root_hash_path)
224✔
1158
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1159

1160
        if (c->root_hash_sig) {
224✔
1161
                _cleanup_free_ char *encoded = NULL;
×
1162
                ssize_t len;
×
1163
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1164
                if (len)
×
1165
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1166
        }
1167

1168
        if (c->root_hash_sig_path)
224✔
1169
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1170

1171
        if (c->root_verity)
224✔
1172
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1173

1174
        STRV_FOREACH(e, c->environment)
224✔
1175
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
×
1176

1177
        STRV_FOREACH(e, c->environment_files)
224✔
1178
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1179

1180
        STRV_FOREACH(e, c->pass_environment)
234✔
1181
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
10✔
1182

1183
        STRV_FOREACH(e, c->unset_environment)
224✔
1184
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
×
1185

1186
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
224✔
1187

1188
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
1,344✔
1189
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
1,120✔
1190

1191
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,130✔
1192
                        fprintf(f,
10✔
1193
                                "%s%s: %s%s\n",
1194
                                prefix,
1195
                                exec_directory_type_to_string(dt),
1196
                                c->directories[dt].items[i].path,
1197
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
10✔
1198

1199
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
1200
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1201
                }
1202
        }
1203

1204
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
224✔
1205

1206
        if (c->memory_ksm >= 0)
224✔
1207
                fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
2✔
1208

1209
        if (c->nice_set)
224✔
1210
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1211

1212
        if (c->oom_score_adjust_set)
224✔
1213
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1214

1215
        if (c->coredump_filter_set)
224✔
1216
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1217

1218
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,808✔
1219
                if (c->rlimit[i]) {
3,584✔
1220
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1221
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1222
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1223
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1224
                }
1225

1226
        if (c->ioprio_set) {
224✔
1227
                _cleanup_free_ char *class_str = NULL;
×
1228

1229
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1230
                if (r >= 0)
×
1231
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1232

1233
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1234
        }
1235

1236
        if (c->cpu_sched_set) {
224✔
1237
                _cleanup_free_ char *policy_str = NULL;
×
1238

1239
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1240
                if (r >= 0)
×
1241
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1242

1243
                fprintf(f,
×
1244
                        "%sCPUSchedulingPriority: %i\n"
1245
                        "%sCPUSchedulingResetOnFork: %s\n",
1246
                        prefix, c->cpu_sched_priority,
×
1247
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1248
        }
1249

1250
        if (c->cpu_set.set) {
224✔
1251
                _cleanup_free_ char *affinity = NULL;
×
1252

1253
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1254
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1255
        }
1256

1257
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1258
                _cleanup_free_ char *nodes = NULL;
1✔
1259

1260
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1261
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1262
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1263
        }
1264

1265
        if (c->timer_slack_nsec != NSEC_INFINITY)
224✔
1266
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1267

1268
        fprintf(f,
224✔
1269
                "%sStandardInput: %s\n"
1270
                "%sStandardOutput: %s\n"
1271
                "%sStandardError: %s\n",
1272
                prefix, exec_input_to_string(c->std_input),
224✔
1273
                prefix, exec_output_to_string(c->std_output),
224✔
1274
                prefix, exec_output_to_string(c->std_error));
224✔
1275

1276
        if (c->std_input == EXEC_INPUT_NAMED_FD)
224✔
1277
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1278
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
224✔
1279
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1280
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
224✔
1281
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1282

1283
        if (c->std_input == EXEC_INPUT_FILE)
224✔
1284
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1285
        if (c->std_output == EXEC_OUTPUT_FILE)
224✔
1286
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1287
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
224✔
1288
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1289
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1290
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1291
        if (c->std_error == EXEC_OUTPUT_FILE)
224✔
1292
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1293
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
224✔
1294
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1295
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1296
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1297

1298
        if (c->tty_path)
224✔
1299
                fprintf(f,
×
1300
                        "%sTTYPath: %s\n"
1301
                        "%sTTYReset: %s\n"
1302
                        "%sTTYVHangup: %s\n"
1303
                        "%sTTYVTDisallocate: %s\n"
1304
                        "%sTTYRows: %u\n"
1305
                        "%sTTYColumns: %u\n",
1306
                        prefix, c->tty_path,
1307
                        prefix, yes_no(c->tty_reset),
×
1308
                        prefix, yes_no(c->tty_vhangup),
×
1309
                        prefix, yes_no(c->tty_vt_disallocate),
×
1310
                        prefix, c->tty_rows,
×
1311
                        prefix, c->tty_cols);
×
1312

1313
        if (IN_SET(c->std_output,
224✔
1314
                   EXEC_OUTPUT_KMSG,
1315
                   EXEC_OUTPUT_JOURNAL,
1316
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1317
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1318
            IN_SET(c->std_error,
17✔
1319
                   EXEC_OUTPUT_KMSG,
1320
                   EXEC_OUTPUT_JOURNAL,
1321
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1322
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1323

1324
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1325

1326
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
207✔
1327
                if (r >= 0)
207✔
1328
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
207✔
1329

1330
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
207✔
1331
                if (r >= 0)
207✔
1332
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
207✔
1333
        }
1334

1335
        if (c->log_level_max >= 0) {
224✔
1336
                _cleanup_free_ char *t = NULL;
1✔
1337

1338
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1339

1340
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1341
        }
1342

1343
        if (c->log_ratelimit.interval > 0)
224✔
1344
                fprintf(f,
×
1345
                        "%sLogRateLimitIntervalSec: %s\n",
1346
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1347

1348
        if (c->log_ratelimit.burst > 0)
224✔
1349
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1350

1351
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
224✔
1352
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1353

1354
                char *pattern;
×
1355
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1356
                        fprintf(f, " %s", pattern);
×
1357
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1358
                        fprintf(f, " ~%s", pattern);
×
1359
                fputc('\n', f);
×
1360
        }
1361

1362
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
228✔
1363
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1364
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1365
                fputc('\n', f);
4✔
1366
        }
1367

1368
        if (c->log_namespace)
224✔
1369
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1370

1371
        if (c->secure_bits) {
224✔
1372
                _cleanup_free_ char *str = NULL;
×
1373

1374
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1375
                if (r >= 0)
×
1376
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1377
        }
1378

1379
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1380
                _cleanup_free_ char *str = NULL;
16✔
1381

1382
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1383
                if (r >= 0)
16✔
1384
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1385
        }
1386

1387
        if (c->capability_ambient_set != 0) {
224✔
1388
                _cleanup_free_ char *str = NULL;
×
1389

1390
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1391
                if (r >= 0)
×
1392
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1393
        }
1394

1395
        if (c->user)
224✔
1396
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1397
        if (c->group)
224✔
1398
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1399

1400
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
447✔
1401

1402
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1403

1404
        if (c->pam_name)
224✔
1405
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1406

1407
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
224✔
1408
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
224✔
1409
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
224✔
1410
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
224✔
1411
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
224✔
1412
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
224✔
1413

1414
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
228✔
1415
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1416
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1417
                        mount->ignore_enoent ? "-": "",
4✔
1418
                        mount->source,
1419
                        mount->destination,
1420
                        mount->recursive ? "rbind" : "norbind");
4✔
1421

1422
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
224✔
1423
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1424
                        tmpfs->path,
1425
                        isempty(tmpfs->options) ? "" : ":",
×
1426
                        strempty(tmpfs->options));
×
1427

1428
        if (c->utmp_id)
224✔
1429
                fprintf(f,
×
1430
                        "%sUtmpIdentifier: %s\n",
1431
                        prefix, c->utmp_id);
1432

1433
        if (c->selinux_context)
224✔
1434
                fprintf(f,
×
1435
                        "%sSELinuxContext: %s%s\n",
1436
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1437

1438
        if (c->apparmor_profile)
224✔
1439
                fprintf(f,
×
1440
                        "%sAppArmorProfile: %s%s\n",
1441
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1442

1443
        if (c->smack_process_label)
224✔
1444
                fprintf(f,
×
1445
                        "%sSmackProcessLabel: %s%s\n",
1446
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1447

1448
        if (c->personality != PERSONALITY_INVALID)
224✔
1449
                fprintf(f,
1✔
1450
                        "%sPersonality: %s\n",
1451
                        prefix, strna(personality_to_string(c->personality)));
1452

1453
        fprintf(f,
224✔
1454
                "%sLockPersonality: %s\n",
1455
                prefix, yes_no(c->lock_personality));
224✔
1456

1457
        if (c->syscall_filter) {
224✔
1458
                fprintf(f,
11✔
1459
                        "%sSystemCallFilter: ",
1460
                        prefix);
1461

1462
                if (!c->syscall_allow_list)
11✔
1463
                        fputc('~', f);
×
1464

1465
#if HAVE_SECCOMP
1466
                void *id, *val;
11✔
1467
                bool first = true;
11✔
1468
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,268✔
1469
                        _cleanup_free_ char *name = NULL;
4,257✔
1470
                        const char *errno_name = NULL;
4,257✔
1471
                        int num = PTR_TO_INT(val);
4,257✔
1472

1473
                        if (first)
4,257✔
1474
                                first = false;
1475
                        else
1476
                                fputc(' ', f);
4,246✔
1477

1478
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,257✔
1479
                        fputs(strna(name), f);
4,257✔
1480

1481
                        if (num >= 0) {
4,257✔
1482
                                errno_name = seccomp_errno_or_action_to_string(num);
×
1483
                                if (errno_name)
×
1484
                                        fprintf(f, ":%s", errno_name);
×
1485
                                else
1486
                                        fprintf(f, ":%d", num);
×
1487
                        }
1488
                }
1489
#endif
1490

1491
                fputc('\n', f);
11✔
1492
        }
1493

1494
        if (c->syscall_archs) {
224✔
1495
                fprintf(f,
11✔
1496
                        "%sSystemCallArchitectures:",
1497
                        prefix);
1498

1499
#if HAVE_SECCOMP
1500
                void *id;
11✔
1501
                SET_FOREACH(id, c->syscall_archs)
22✔
1502
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1503
#endif
1504
                fputc('\n', f);
11✔
1505
        }
1506

1507
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1508
                _cleanup_free_ char *s = NULL;
12✔
1509

1510
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1511
                if (r >= 0)
12✔
1512
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1513
                                prefix, strna(s));
1514
        }
1515

1516
#if HAVE_LIBBPF
1517
        if (exec_context_restrict_filesystems_set(c)) {
224✔
1518
                char *fs;
×
1519
                SET_FOREACH(fs, c->restrict_filesystems)
×
1520
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1521
        }
1522
#endif
1523

1524
        if (c->network_namespace_path)
224✔
1525
                fprintf(f,
×
1526
                        "%sNetworkNamespacePath: %s\n",
1527
                        prefix, c->network_namespace_path);
1528

1529
        if (c->syscall_errno > 0) {
224✔
1530
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1531

1532
#if HAVE_SECCOMP
1533
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
223✔
1534
                if (errno_name)
223✔
1535
                        fputs(errno_name, f);
223✔
1536
                else
1537
                        fprintf(f, "%d", c->syscall_errno);
×
1538
#endif
1539
                fputc('\n', f);
223✔
1540
        }
1541

1542
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
224✔
1543
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1544
                        mount->ignore_enoent ? "-": "",
×
1545
                        mount->source,
1546
                        mount->destination);
1547
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1548
                        fprintf(f, ":%s:%s",
×
1549
                                partition_designator_to_string(o->partition_designator),
1550
                                strempty(o->options));
×
1551
                fprintf(f, "\n");
×
1552
        }
1553

1554
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
224✔
1555
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1556
                        mount->ignore_enoent ? "-": "",
×
1557
                        mount->source);
1558
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1559
                        fprintf(f, ":%s:%s",
×
1560
                                partition_designator_to_string(o->partition_designator),
1561
                                strempty(o->options));
×
1562
                fprintf(f, "\n");
×
1563
        }
1564

1565
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1566
}
224✔
1567

1568
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1569
        assert(c);
×
1570

1571
        /* Returns true if the process forked off would run under
1572
         * an unchanged UID or as root. */
1573

1574
        if (!c->user)
×
1575
                return true;
1576

1577
        if (STR_IN_SET(c->user, "root", "0"))
×
1578
                return true;
×
1579

1580
        return false;
×
1581
}
1582

1583
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,520✔
1584
        int p;
2,520✔
1585

1586
        assert(c);
2,520✔
1587

1588
        if (c->ioprio_set)
2,520✔
1589
                return c->ioprio;
18✔
1590

1591
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,502✔
1592
        if (p < 0)
2,502✔
1593
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1594

1595
        return ioprio_normalize(p);
2,502✔
1596
}
1597

1598
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
33,744✔
1599
        assert(c);
33,744✔
1600

1601
        /* Explicit setting wins */
1602
        if (c->mount_apivfs >= 0)
33,744✔
1603
                return c->mount_apivfs > 0;
122✔
1604

1605
        /* Default to "yes" if root directory or image are specified */
1606
        if (exec_context_with_rootfs(c))
33,622✔
1607
                return true;
54✔
1608

1609
        return false;
1610
}
1611

1612
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
28,247✔
1613
        assert(c);
28,247✔
1614

1615
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1616
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1617
        if (c->log_namespace)
28,247✔
1618
                return true;
1619

1620
        if (c->bind_log_sockets >= 0)
28,239✔
1621
                return c->bind_log_sockets > 0;
2✔
1622

1623
        if (exec_context_get_effective_mount_apivfs(c))
28,237✔
1624
                return true;
1625

1626
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1627
        if (exec_context_with_rootfs(c) && c->private_devices)
28,170✔
1628
                return true;
×
1629

1630
        return false;
1631
}
1632

1633
void exec_context_free_log_extra_fields(ExecContext *c) {
46,168✔
1634
        assert(c);
46,168✔
1635

1636
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
46,173✔
1637
                free(field->iov_base);
5✔
1638

1639
        c->log_extra_fields = mfree(c->log_extra_fields);
46,168✔
1640
        c->n_log_extra_fields = 0;
46,168✔
1641
}
46,168✔
1642

1643
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,509✔
1644
        _cleanup_close_ int fd = -EBADF;
1,509✔
1645
        const char *path;
1,509✔
1646
        struct stat st;
1,509✔
1647
        int r;
1,509✔
1648

1649
        assert(c);
1,509✔
1650

1651
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1652
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,509✔
1653

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

1660
        path = exec_context_tty_path(c);
49✔
1661
        if (!path)
49✔
1662
                return;
1663

1664
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
49✔
1665
        if (fd < 0)
49✔
1666
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1667
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1668
                                             path);
1669

1670
        if (fstat(fd, &st) < 0)
49✔
1671
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1672

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

1681
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
49✔
1682
        if (r < 0)
49✔
1683
                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);
49✔
1684
}
1685

1686
int exec_context_get_clean_directories(
×
1687
                ExecContext *c,
1688
                char **prefix,
1689
                ExecCleanMask mask,
1690
                char ***ret) {
1691

1692
        _cleanup_strv_free_ char **l = NULL;
×
1693
        int r;
×
1694

1695
        assert(c);
×
1696
        assert(prefix);
×
1697
        assert(ret);
×
1698

1699
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1700
                if (!BIT_SET(mask, t))
×
1701
                        continue;
×
1702

1703
                if (!prefix[t])
×
1704
                        continue;
×
1705

1706
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1707
                        char *j;
×
1708

1709
                        j = path_join(prefix[t], i->path);
×
1710
                        if (!j)
×
1711
                                return -ENOMEM;
1712

1713
                        r = strv_consume(&l, j);
×
1714
                        if (r < 0)
×
1715
                                return r;
1716

1717
                        /* Also remove private directories unconditionally. */
1718
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1719
                                j = path_join(prefix[t], "private", i->path);
×
1720
                                if (!j)
×
1721
                                        return -ENOMEM;
1722

1723
                                r = strv_consume(&l, j);
×
1724
                                if (r < 0)
×
1725
                                        return r;
1726
                        }
1727

1728
                        STRV_FOREACH(symlink, i->symlinks) {
×
1729
                                j = path_join(prefix[t], *symlink);
×
1730
                                if (!j)
×
1731
                                        return -ENOMEM;
1732

1733
                                r = strv_consume(&l, j);
×
1734
                                if (r < 0)
×
1735
                                        return r;
1736
                        }
1737
                }
1738
        }
1739

1740
        *ret = TAKE_PTR(l);
×
1741
        return 0;
×
1742
}
1743

1744
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,240✔
1745
        ExecCleanMask mask = 0;
1,240✔
1746

1747
        assert(c);
1,240✔
1748
        assert(ret);
1,240✔
1749

1750
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,440✔
1751
                if (c->directories[t].n_items > 0)
6,200✔
1752
                        mask |= 1U << t;
257✔
1753

1754
        *ret = mask;
1,240✔
1755
        return 0;
1,240✔
1756
}
1757

1758
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,260✔
1759
        int n = 0, r;
1,260✔
1760

1761
        assert(c);
1,260✔
1762

1763
        if (c->oom_score_adjust_set)
1,260✔
1764
                return c->oom_score_adjust;
340✔
1765

1766
        r = get_oom_score_adjust(&n);
920✔
1767
        if (r < 0)
920✔
1768
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1769

1770
        return n;
920✔
1771
}
1772

1773
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,260✔
1774
        _cleanup_free_ char *t = NULL;
1,260✔
1775
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,260✔
1776
        int r;
1,260✔
1777

1778
        assert(c);
1,260✔
1779

1780
        if (c->coredump_filter_set)
1,260✔
1781
                return c->coredump_filter;
×
1782

1783
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,260✔
1784
        if (r < 0)
1,260✔
1785
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1786
        else {
1787
                r = safe_atoux64(t, &n);
1,260✔
1788
                if (r < 0)
1,260✔
1789
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1790
        }
1791

1792
        return n;
1,260✔
1793
}
1794

1795
int exec_context_get_nice(const ExecContext *c) {
1,260✔
1796
        int n;
1,260✔
1797

1798
        assert(c);
1,260✔
1799

1800
        if (c->nice_set)
1,260✔
1801
                return c->nice;
6✔
1802

1803
        errno = 0;
1,254✔
1804
        n = getpriority(PRIO_PROCESS, 0);
1,254✔
1805
        if (errno > 0) {
1,254✔
1806
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1807
                n = 0;
1808
        }
1809

1810
        return n;
1811
}
1812

1813
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,260✔
1814
        int n;
1,260✔
1815

1816
        assert(c);
1,260✔
1817

1818
        if (c->cpu_sched_set)
1,260✔
1819
                return c->cpu_sched_policy;
×
1820

1821
        n = sched_getscheduler(0);
1,260✔
1822
        if (n < 0)
1,260✔
1823
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1824

1825
        return n < 0 ? SCHED_OTHER : n;
1,260✔
1826
}
1827

1828
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,260✔
1829
        struct sched_param p = {};
1,260✔
1830
        int r;
1,260✔
1831

1832
        assert(c);
1,260✔
1833

1834
        if (c->cpu_sched_set)
1,260✔
1835
                return c->cpu_sched_priority;
×
1836

1837
        r = sched_getparam(0, &p);
1,260✔
1838
        if (r < 0)
1,260✔
1839
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1840

1841
        return r >= 0 ? p.sched_priority : 0;
1,260✔
1842
}
1843

1844
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,260✔
1845
        int r;
1,260✔
1846

1847
        assert(c);
1,260✔
1848

1849
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,260✔
1850
                return c->timer_slack_nsec;
1851

1852
        r = prctl(PR_GET_TIMERSLACK);
1,260✔
1853
        if (r < 0)
1,260✔
1854
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1855

1856
        return (uint64_t) MAX(r, 0);
1,260✔
1857
}
1858

1859
bool exec_context_get_set_login_environment(const ExecContext *c) {
10,813✔
1860
        assert(c);
10,813✔
1861

1862
        if (c->set_login_environment >= 0)
10,813✔
1863
                return c->set_login_environment;
×
1864

1865
        return c->user || c->dynamic_user || c->pam_name;
19,484✔
1866
}
1867

1868
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,260✔
1869
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1870

1871
        assert(c);
1,260✔
1872

1873
#if HAVE_SECCOMP
1874
        void *id, *val;
1,260✔
1875
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
18,798✔
1876
                _cleanup_free_ char *name = NULL;
17,538✔
1877
                const char *e = NULL;
17,538✔
1878
                char *s;
17,538✔
1879
                int num = PTR_TO_INT(val);
17,538✔
1880

1881
                if (c->syscall_allow_list && num >= 0)
17,538✔
1882
                        /* syscall with num >= 0 in allow-list is denied. */
1883
                        continue;
×
1884

1885
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17,538✔
1886
                if (!name)
17,538✔
1887
                        continue;
×
1888

1889
                if (num >= 0) {
17,538✔
1890
                        e = seccomp_errno_or_action_to_string(num);
×
1891
                        if (e) {
×
1892
                                s = strjoin(name, ":", e);
×
1893
                                if (!s)
×
1894
                                        return NULL;
1895
                        } else {
1896
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1897
                                        return NULL;
1898
                        }
1899
                } else
1900
                        s = TAKE_PTR(name);
17,538✔
1901

1902
                if (strv_consume(&l, s) < 0)
17,538✔
1903
                        return NULL;
1904
        }
1905

1906
        strv_sort(l);
1,260✔
1907
#endif
1908

1909
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1910
}
1911

1912
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,260✔
1913
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1914

1915
        assert(c);
1,260✔
1916

1917
#if HAVE_SECCOMP
1918
        void *id;
1,260✔
1919
        SET_FOREACH(id, c->syscall_archs) {
1,311✔
1920
                const char *name;
51✔
1921

1922
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
51✔
1923
                if (!name)
51✔
1924
                        continue;
×
1925

1926
                if (strv_extend(&l, name) < 0)
51✔
1927
                        return NULL;
×
1928
        }
1929

1930
        strv_sort(l);
1,260✔
1931
#endif
1932

1933
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1934
}
1935

1936
char** exec_context_get_syscall_log(const ExecContext *c) {
1,260✔
1937
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1938

1939
        assert(c);
1,260✔
1940

1941
#if HAVE_SECCOMP
1942
        void *id, *val;
1,260✔
1943
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,260✔
1944
                char *name = NULL;
×
1945

1946
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1947
                if (!name)
×
1948
                        continue;
×
1949

1950
                if (strv_consume(&l, name) < 0)
×
1951
                        return NULL;
×
1952
        }
1953

1954
        strv_sort(l);
1,260✔
1955
#endif
1956

1957
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1958
}
1959

1960
char** exec_context_get_address_families(const ExecContext *c) {
1,260✔
1961
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1962
        void *af;
1,260✔
1963

1964
        assert(c);
1,260✔
1965

1966
        SET_FOREACH(af, c->address_families) {
1,410✔
1967
                const char *name;
150✔
1968

1969
                name = af_to_name(PTR_TO_INT(af));
150✔
1970
                if (!name)
150✔
1971
                        continue;
×
1972

1973
                if (strv_extend(&l, name) < 0)
150✔
1974
                        return NULL;
×
1975
        }
1976

1977
        strv_sort(l);
1,260✔
1978

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

1982
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,260✔
1983
        assert(c);
1,260✔
1984

1985
#if HAVE_LIBBPF
1986
        char **l = set_get_strv(c->restrict_filesystems);
1,260✔
1987
        if (!l)
1,260✔
1988
                return NULL;
1989

1990
        return strv_sort(l);
1,260✔
1991
#else
1992
        return strv_new(NULL);
1993
#endif
1994
}
1995

1996
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,565✔
1997
        assert(s);
4,565✔
1998

1999
        *s = (ExecStatus) {
4,565✔
2000
                .pid = pid,
2001
        };
2002

2003
        if (ts)
4,565✔
2004
                s->start_timestamp = *ts;
4,565✔
2005
        else
2006
                dual_timestamp_now(&s->start_timestamp);
×
2007
}
4,565✔
2008

2009
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,031✔
2010
        assert(s);
2,031✔
2011

2012
        if (s->pid != pid)
2,031✔
2013
                *s = (ExecStatus) {
6✔
2014
                        .pid = pid,
2015
                };
2016

2017
        dual_timestamp_now(&s->exit_timestamp);
2,031✔
2018

2019
        s->code = code;
2,031✔
2020
        s->status = status;
2,031✔
2021

2022
        if (context && context->utmp_id)
2,031✔
2023
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2024
}
2,031✔
2025

2026
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,734✔
2027
        assert(s);
7,734✔
2028
        assert(ucred);
7,734✔
2029
        assert(ts);
7,734✔
2030

2031
        if (ucred->pid != s->pid)
7,734✔
2032
                *s = (ExecStatus) {
9✔
2033
                        .pid = ucred->pid,
2034
                };
2035

2036
        s->handoff_timestamp = *ts;
7,734✔
2037
}
7,734✔
2038

2039
void exec_status_reset(ExecStatus *s) {
24,726✔
2040
        assert(s);
24,726✔
2041

2042
        *s = (ExecStatus) {};
24,726✔
2043
}
24,726✔
2044

2045
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2046
        assert(s);
94✔
2047
        assert(f);
94✔
2048

2049
        if (s->pid <= 0)
94✔
2050
                return;
2051

2052
        prefix = strempty(prefix);
10✔
2053

2054
        fprintf(f,
10✔
2055
                "%sPID: "PID_FMT"\n",
2056
                prefix, s->pid);
2057

2058
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2059
                fprintf(f,
10✔
2060
                        "%sStart Timestamp: %s\n",
2061
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2062

2063
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2064
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2065
                fprintf(f,
10✔
2066
                        "%sHandoff Timestamp: %s since start\n",
2067
                        prefix,
2068
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2069
        else
2070
                fprintf(f,
×
2071
                        "%sHandoff Timestamp: %s\n",
2072
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2073

2074
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2075

2076
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
2077
                        fprintf(f,
×
2078
                                "%sExit Timestamp: %s since handoff\n",
2079
                                prefix,
2080
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
2081
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2082
                        fprintf(f,
×
2083
                                "%sExit Timestamp: %s since start\n",
2084
                                prefix,
2085
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2086
                else
2087
                        fprintf(f,
×
2088
                                "%sExit Timestamp: %s\n",
2089
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2090

2091
                fprintf(f,
×
2092
                        "%sExit Code: %s\n"
2093
                        "%sExit Status: %i\n",
2094
                        prefix, sigchld_code_to_string(s->code),
×
2095
                        prefix, s->status);
×
2096
        }
2097
}
2098

2099
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2100
        _cleanup_free_ char *cmd = NULL;
188✔
2101
        const char *prefix2;
94✔
2102

2103
        assert(c);
94✔
2104
        assert(f);
94✔
2105

2106
        prefix = strempty(prefix);
94✔
2107
        prefix2 = strjoina(prefix, "\t");
470✔
2108

2109
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2110

2111
        fprintf(f,
94✔
2112
                "%sCommand Line: %s\n",
2113
                prefix, strnull(cmd));
2114

2115
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2116
}
94✔
2117

2118
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2119
        assert(f);
91✔
2120

2121
        prefix = strempty(prefix);
91✔
2122

2123
        LIST_FOREACH(command, i, c)
185✔
2124
                exec_command_dump(i, f, prefix);
94✔
2125
}
91✔
2126

2127
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
16,460✔
2128
        ExecCommand *end;
16,460✔
2129

2130
        assert(l);
16,460✔
2131
        assert(e);
16,460✔
2132

2133
        if (*l) {
16,460✔
2134
                /* It's kind of important, that we keep the order here */
2135
                end = LIST_FIND_TAIL(command, *l);
371✔
2136
                LIST_INSERT_AFTER(command, *l, end, e);
134✔
2137
        } else
2138
                *l = e;
16,326✔
2139
}
16,460✔
2140

2141
int exec_command_set(ExecCommand *c, const char *path, ...) {
172✔
2142
        va_list ap;
172✔
2143
        char **l, *p;
172✔
2144

2145
        assert(c);
172✔
2146
        assert(path);
172✔
2147

2148
        va_start(ap, path);
172✔
2149
        l = strv_new_ap(path, ap);
172✔
2150
        va_end(ap);
172✔
2151

2152
        if (!l)
172✔
2153
                return -ENOMEM;
172✔
2154

2155
        p = strdup(path);
172✔
2156
        if (!p) {
172✔
2157
                strv_free(l);
×
2158
                return -ENOMEM;
×
2159
        }
2160

2161
        free_and_replace(c->path, p);
172✔
2162

2163
        return strv_free_and_replace(c->argv, l);
172✔
2164
}
2165

2166
int exec_command_append(ExecCommand *c, const char *path, ...) {
247✔
2167
        char **l;
247✔
2168
        va_list ap;
247✔
2169
        int r;
247✔
2170

2171
        assert(c);
247✔
2172
        assert(path);
247✔
2173

2174
        va_start(ap, path);
247✔
2175
        l = strv_new_ap(path, ap);
247✔
2176
        va_end(ap);
247✔
2177

2178
        if (!l)
247✔
2179
                return -ENOMEM;
247✔
2180

2181
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
247✔
2182
        if (r < 0)
247✔
2183
                return r;
×
2184

2185
        return 0;
2186
}
2187

2188
static char *destroy_tree(char *path) {
269✔
2189
        if (!path)
269✔
2190
                return NULL;
2191

2192
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
84✔
2193
                log_debug("Spawning process to nuke '%s'", path);
84✔
2194

2195
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
84✔
2196
        }
2197

2198
        return mfree(path);
84✔
2199
}
2200

2201
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
128,683✔
2202
        assert(rt);
128,683✔
2203

2204
        if (rt->manager)
128,683✔
2205
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
172✔
2206

2207
        rt->id = mfree(rt->id);
128,683✔
2208
        rt->tmp_dir = mfree(rt->tmp_dir);
128,683✔
2209
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
128,683✔
2210
        safe_close_pair(rt->netns_storage_socket);
128,683✔
2211
        safe_close_pair(rt->ipcns_storage_socket);
128,683✔
2212
}
128,683✔
2213

2214
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
128,655✔
2215
        if (!rt)
128,655✔
2216
                return NULL;
2217

2218
        exec_shared_runtime_done(rt);
128,655✔
2219
        return mfree(rt);
128,655✔
2220
}
2221

2222
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
173✔
2223
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
131,785✔
2224

2225
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
49✔
2226
        if (!rt)
49✔
2227
                return NULL;
2228

2229
        assert(rt->n_ref > 0);
48✔
2230
        rt->n_ref--;
48✔
2231

2232
        if (rt->n_ref > 0)
48✔
2233
                return NULL;
2234

2235
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
48✔
2236
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
48✔
2237

2238
        return exec_shared_runtime_free(rt);
48✔
2239
}
2240

2241
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
128,655✔
2242
        _cleanup_free_ char *id_copy = NULL;
257,310✔
2243
        ExecSharedRuntime *n;
128,655✔
2244

2245
        assert(ret);
128,655✔
2246

2247
        id_copy = strdup(id);
128,655✔
2248
        if (!id_copy)
128,655✔
2249
                return -ENOMEM;
2250

2251
        n = new(ExecSharedRuntime, 1);
128,655✔
2252
        if (!n)
128,655✔
2253
                return -ENOMEM;
2254

2255
        *n = (ExecSharedRuntime) {
128,655✔
2256
                .id = TAKE_PTR(id_copy),
128,655✔
2257
                .netns_storage_socket = EBADF_PAIR,
2258
                .ipcns_storage_socket = EBADF_PAIR,
2259
        };
2260

2261
        *ret = n;
128,655✔
2262
        return 0;
128,655✔
2263
}
2264

2265
static int exec_shared_runtime_add(
172✔
2266
                Manager *m,
2267
                const char *id,
2268
                char **tmp_dir,
2269
                char **var_tmp_dir,
2270
                int netns_storage_socket[2],
2271
                int ipcns_storage_socket[2],
2272
                ExecSharedRuntime **ret) {
2273

2274
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
172✔
2275
        int r;
172✔
2276

2277
        assert(m);
172✔
2278
        assert(id);
172✔
2279

2280
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2281

2282
        r = exec_shared_runtime_allocate(&rt, id);
172✔
2283
        if (r < 0)
172✔
2284
                return r;
2285

2286
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
172✔
2287
        if (r < 0)
172✔
2288
                return r;
2289

2290
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
172✔
2291
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
172✔
2292
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
172✔
2293

2294
        if (netns_storage_socket) {
172✔
2295
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
172✔
2296
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
172✔
2297
        }
2298

2299
        if (ipcns_storage_socket) {
172✔
2300
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
172✔
2301
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
172✔
2302
        }
2303

2304
        rt->manager = m;
172✔
2305

2306
        if (ret)
172✔
2307
                *ret = rt;
69✔
2308
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2309
        TAKE_PTR(rt);
172✔
2310
        return 0;
172✔
2311
}
2312

2313
static int exec_shared_runtime_make(
6,081✔
2314
                Manager *m,
2315
                const ExecContext *c,
2316
                const char *id,
2317
                ExecSharedRuntime **ret) {
2318

2319
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
6,081✔
2320
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
12,162✔
2321
        int r;
6,081✔
2322

2323
        assert(m);
6,081✔
2324
        assert(c);
6,081✔
2325
        assert(id);
6,081✔
2326

2327
        /* It is not necessary to create ExecSharedRuntime object. */
2328
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
6,081✔
2329
                *ret = NULL;
6,012✔
2330
                return 0;
6,012✔
2331
        }
2332

2333
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
132✔
2334
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
63✔
2335
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2336
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2337
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
63✔
2338
                if (r < 0)
63✔
2339
                        return r;
2340
        }
2341

2342
        if (exec_needs_network_namespace(c))
69✔
2343
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2344
                        return -errno;
×
2345

2346
        if (exec_needs_ipc_namespace(c))
69✔
2347
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2348
                        return -errno;
×
2349

2350
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
69✔
2351
        if (r < 0)
69✔
2352
                return r;
×
2353

2354
        return 1;
2355
}
2356

2357
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,184✔
2358
        ExecSharedRuntime *rt;
6,184✔
2359
        int r;
6,184✔
2360

2361
        assert(m);
6,184✔
2362
        assert(id);
6,184✔
2363
        assert(ret);
6,184✔
2364

2365
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,184✔
2366
        if (rt)
6,184✔
2367
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2368
                goto ref;
103✔
2369

2370
        if (!create) {
6,081✔
2371
                *ret = NULL;
×
2372
                return 0;
×
2373
        }
2374

2375
        /* If not found, then create a new object. */
2376
        r = exec_shared_runtime_make(m, c, id, &rt);
6,081✔
2377
        if (r < 0)
6,081✔
2378
                return r;
2379
        if (r == 0) {
6,081✔
2380
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2381
                *ret = NULL;
6,012✔
2382
                return 0;
6,012✔
2383
        }
2384

2385
ref:
69✔
2386
        /* increment reference counter. */
2387
        rt->n_ref++;
172✔
2388
        *ret = rt;
172✔
2389
        return 1;
172✔
2390
}
2391

2392
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
73✔
2393
        ExecSharedRuntime *rt;
73✔
2394

2395
        assert(m);
73✔
2396
        assert(f);
73✔
2397
        assert(fds);
73✔
2398

2399
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
197✔
2400
                fprintf(f, "exec-runtime=%s", rt->id);
124✔
2401

2402
                if (rt->tmp_dir)
124✔
2403
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
124✔
2404

2405
                if (rt->var_tmp_dir)
124✔
2406
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
124✔
2407

2408
                if (rt->netns_storage_socket[0] >= 0) {
124✔
2409
                        int copy;
2✔
2410

2411
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2412
                        if (copy < 0)
2✔
2413
                                return copy;
×
2414

2415
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2416
                }
2417

2418
                if (rt->netns_storage_socket[1] >= 0) {
124✔
2419
                        int copy;
2✔
2420

2421
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2422
                        if (copy < 0)
2✔
2423
                                return copy;
2424

2425
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2426
                }
2427

2428
                if (rt->ipcns_storage_socket[0] >= 0) {
124✔
2429
                        int copy;
×
2430

2431
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2432
                        if (copy < 0)
×
2433
                                return copy;
2434

2435
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2436
                }
2437

2438
                if (rt->ipcns_storage_socket[1] >= 0) {
124✔
2439
                        int copy;
×
2440

2441
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2442
                        if (copy < 0)
×
2443
                                return copy;
2444

2445
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2446
                }
2447

2448
                fputc('\n', f);
124✔
2449
        }
2450

2451
        return 0;
73✔
2452
}
2453

2454
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
131,613✔
2455
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
131,613✔
2456
        ExecSharedRuntime *rt = NULL;
131,613✔
2457
        int r;
131,613✔
2458

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

2464
        assert(u);
131,613✔
2465
        assert(key);
131,613✔
2466
        assert(value);
131,613✔
2467

2468
        /* Manager manages ExecSharedRuntime objects by the unit id.
2469
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2470
        if (isempty(u->id)) {
131,613✔
2471
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2472
                return 0;
×
2473
        }
2474

2475
        if (u->manager) {
131,613✔
2476
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
131,613✔
2477
                        return log_oom();
×
2478

2479
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
131,613✔
2480
        }
2481
        if (!rt) {
131,613✔
2482
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
128,483✔
2483
                        return log_oom();
×
2484

2485
                rt = rt_create;
128,483✔
2486
        }
2487

2488
        if (streq(key, "tmp-dir")) {
131,613✔
2489
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2490
                        return -ENOMEM;
2491

2492
        } else if (streq(key, "var-tmp-dir")) {
131,613✔
2493
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2494
                        return -ENOMEM;
2495

2496
        } else if (streq(key, "netns-socket-0")) {
131,613✔
2497

2498
                safe_close(rt->netns_storage_socket[0]);
×
2499
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2500
                if (rt->netns_storage_socket[0] < 0)
×
2501
                        return 0;
2502

2503
        } else if (streq(key, "netns-socket-1")) {
131,613✔
2504

2505
                safe_close(rt->netns_storage_socket[1]);
×
2506
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2507
                if (rt->netns_storage_socket[1] < 0)
×
2508
                        return 0;
2509
        } else
2510
                return 0;
2511

2512
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2513
        if (rt_create && u->manager) {
×
2514
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2515
                if (r < 0) {
×
2516
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2517
                        return 0;
×
2518
                }
2519

2520
                rt_create->manager = u->manager;
×
2521

2522
                /* Avoid cleanup */
2523
                TAKE_PTR(rt_create);
×
2524
        }
2525

2526
        return 1;
2527
}
2528

2529
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
103✔
2530
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
103✔
2531
        char *id = NULL;
103✔
2532
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
103✔
2533
        const char *p, *v = ASSERT_PTR(value);
103✔
2534
        size_t n;
103✔
2535

2536
        assert(m);
103✔
2537
        assert(fds);
103✔
2538

2539
        n = strcspn(v, " ");
103✔
2540
        id = strndupa_safe(v, n);
103✔
2541
        if (v[n] != ' ')
103✔
2542
                goto finalize;
×
2543
        p = v + n + 1;
103✔
2544

2545
        v = startswith(p, "tmp-dir=");
103✔
2546
        if (v) {
103✔
2547
                n = strcspn(v, " ");
103✔
2548
                tmp_dir = strndup(v, n);
103✔
2549
                if (!tmp_dir)
103✔
2550
                        return log_oom();
×
2551
                if (v[n] != ' ')
103✔
2552
                        goto finalize;
×
2553
                p = v + n + 1;
103✔
2554
        }
2555

2556
        v = startswith(p, "var-tmp-dir=");
103✔
2557
        if (v) {
103✔
2558
                n = strcspn(v, " ");
103✔
2559
                var_tmp_dir = strndup(v, n);
103✔
2560
                if (!var_tmp_dir)
103✔
2561
                        return log_oom();
×
2562
                if (v[n] != ' ')
103✔
2563
                        goto finalize;
102✔
2564
                p = v + n + 1;
1✔
2565
        }
2566

2567
        v = startswith(p, "netns-socket-0=");
1✔
2568
        if (v) {
1✔
2569
                char *buf;
1✔
2570

2571
                n = strcspn(v, " ");
1✔
2572
                buf = strndupa_safe(v, n);
1✔
2573

2574
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2575
                if (netns_fdpair[0] < 0)
1✔
2576
                        return netns_fdpair[0];
2577
                if (v[n] != ' ')
1✔
2578
                        goto finalize;
×
2579
                p = v + n + 1;
1✔
2580
        }
2581

2582
        v = startswith(p, "netns-socket-1=");
1✔
2583
        if (v) {
1✔
2584
                char *buf;
1✔
2585

2586
                n = strcspn(v, " ");
1✔
2587
                buf = strndupa_safe(v, n);
1✔
2588

2589
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2590
                if (netns_fdpair[1] < 0)
1✔
2591
                        return netns_fdpair[1];
2592
                if (v[n] != ' ')
1✔
2593
                        goto finalize;
1✔
2594
                p = v + n + 1;
×
2595
        }
2596

2597
        v = startswith(p, "ipcns-socket-0=");
×
2598
        if (v) {
×
2599
                char *buf;
×
2600

2601
                n = strcspn(v, " ");
×
2602
                buf = strndupa_safe(v, n);
×
2603

2604
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2605
                if (ipcns_fdpair[0] < 0)
×
2606
                        return ipcns_fdpair[0];
2607
                if (v[n] != ' ')
×
2608
                        goto finalize;
×
2609
                p = v + n + 1;
×
2610
        }
2611

2612
        v = startswith(p, "ipcns-socket-1=");
×
2613
        if (v) {
×
2614
                char *buf;
×
2615

2616
                n = strcspn(v, " ");
×
2617
                buf = strndupa_safe(v, n);
×
2618

2619
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2620
                if (ipcns_fdpair[1] < 0)
×
2621
                        return ipcns_fdpair[1];
2622
        }
2623

2624
finalize:
×
2625
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
103✔
2626
        if (r < 0)
103✔
2627
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2628
        return 0;
2629
}
2630

2631
void exec_shared_runtime_vacuum(Manager *m) {
1,444✔
2632
        ExecSharedRuntime *rt;
1,444✔
2633

2634
        assert(m);
1,444✔
2635

2636
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2637

2638
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,547✔
2639
                if (rt->n_ref > 0)
103✔
2640
                        continue;
103✔
2641

2642
                (void) exec_shared_runtime_free(rt);
×
2643
        }
2644
}
1,444✔
2645

2646
int exec_runtime_make(
6,184✔
2647
                const Unit *unit,
2648
                const ExecContext *context,
2649
                ExecSharedRuntime *shared,
2650
                DynamicCreds *creds,
2651
                ExecRuntime **ret) {
2652
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
6,184✔
2653
        _cleanup_free_ char *ephemeral = NULL;
6,184✔
2654
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
6,184✔
2655
        int r;
6,184✔
2656

2657
        assert(unit);
6,184✔
2658
        assert(context);
6,184✔
2659
        assert(ret);
6,184✔
2660

2661
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,184✔
2662
                *ret = NULL;
6,011✔
2663
                return 0;
6,011✔
2664
        }
2665

2666
        if (exec_needs_ephemeral(context)) {
173✔
2667
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2668
                if (r < 0)
×
2669
                        return r;
2670

2671
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2672
                if (r < 0)
×
2673
                        return r;
2674

2675
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2676
                        return -errno;
×
2677
        }
2678

2679
        rt = new(ExecRuntime, 1);
173✔
2680
        if (!rt)
173✔
2681
                return -ENOMEM;
2682

2683
        *rt = (ExecRuntime) {
173✔
2684
                .shared = shared,
2685
                .dynamic_creds = creds,
2686
                .ephemeral_copy = TAKE_PTR(ephemeral),
173✔
2687
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
173✔
2688
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
173✔
2689
        };
2690

2691
        *ret = TAKE_PTR(rt);
173✔
2692
        return 1;
173✔
2693
}
2694

2695
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
46,187✔
2696
        if (!rt)
46,187✔
2697
                return NULL;
2698

2699
        exec_shared_runtime_unref(rt->shared);
173✔
2700
        dynamic_creds_unref(rt->dynamic_creds);
173✔
2701

2702
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
173✔
2703

2704
        safe_close_pair(rt->ephemeral_storage_socket);
173✔
2705
        return mfree(rt);
173✔
2706
}
2707

2708
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
4,971✔
2709
        if (!rt)
4,971✔
2710
                return NULL;
2711

2712
        rt->shared = exec_shared_runtime_destroy(rt->shared);
49✔
2713
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
49✔
2714
        return exec_runtime_free(rt);
49✔
2715
}
2716

2717
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2718
        if (!rt)
28✔
2719
                return;
2720

2721
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2722
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2723
}
2724

2725
void exec_params_shallow_clear(ExecParameters *p) {
2,227✔
2726
        if (!p)
2,227✔
2727
                return;
2728

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

2732
        p->environment = strv_free(p->environment);
2,227✔
2733
        p->fd_names = strv_free(p->fd_names);
2,227✔
2734
        p->files_env = strv_free(p->files_env);
2,227✔
2735
        p->fds = mfree(p->fds);
2,227✔
2736
        p->exec_fd = safe_close(p->exec_fd);
2,227✔
2737
        p->user_lookup_fd = -EBADF;
2,227✔
2738
        p->bpf_restrict_fs_map_fd = -EBADF;
2,227✔
2739
        p->unit_id = mfree(p->unit_id);
2,227✔
2740
        p->invocation_id = SD_ID128_NULL;
2,227✔
2741
        p->invocation_id_string[0] = '\0';
2,227✔
2742
        p->confirm_spawn = mfree(p->confirm_spawn);
2,227✔
2743
}
2744

2745
void exec_params_deep_clear(ExecParameters *p) {
28✔
2746
        if (!p)
28✔
2747
                return;
2748

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

2753
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2754

2755
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2756

2757
        if (p->prefix) {
28✔
2758
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2759
                p->prefix = mfree(p->prefix);
28✔
2760
        }
2761

2762
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2763
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2764

2765
        if (p->idle_pipe) {
28✔
2766
                close_many_and_free(p->idle_pipe, 4);
×
2767
                p->idle_pipe = NULL;
×
2768
        }
2769

2770
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2771
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2772
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2773

2774
        p->notify_socket = mfree(p->notify_socket);
28✔
2775

2776
        open_file_free_many(&p->open_files);
28✔
2777

2778
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2779

2780
        exec_params_shallow_clear(p);
28✔
2781
}
2782

2783
void exec_directory_done(ExecDirectory *d) {
230,830✔
2784
        if (!d)
230,830✔
2785
                return;
2786

2787
        FOREACH_ARRAY(i, d->items, d->n_items) {
232,636✔
2788
                free(i->path);
1,806✔
2789
                strv_free(i->symlinks);
1,806✔
2790
        }
2791

2792
        d->items = mfree(d->items);
230,830✔
2793
        d->n_items = 0;
230,830✔
2794
        d->mode = 0755;
230,830✔
2795
}
2796

2797
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,514✔
2798
        assert(d);
5,514✔
2799
        assert(path);
5,514✔
2800

2801
        FOREACH_ARRAY(i, d->items, d->n_items)
7,858✔
2802
                if (path_equal(i->path, path))
2,359✔
2803
                        return i;
2804

2805
        return NULL;
2806
}
2807

2808
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,514✔
2809
        _cleanup_strv_free_ char **s = NULL;
×
2810
        _cleanup_free_ char *p = NULL;
5,514✔
2811
        ExecDirectoryItem *existing;
5,514✔
2812
        int r;
5,514✔
2813

2814
        assert(d);
5,514✔
2815
        assert(path);
5,514✔
2816

2817
        existing = exec_directory_find(d, path);
5,514✔
2818
        if (existing) {
5,514✔
2819
                r = strv_extend(&existing->symlinks, symlink);
15✔
2820
                if (r < 0)
15✔
2821
                        return r;
2822

2823
                existing->flags |= flags;
15✔
2824

2825
                return 0; /* existing item is updated */
15✔
2826
        }
2827

2828
        p = strdup(path);
5,499✔
2829
        if (!p)
5,499✔
2830
                return -ENOMEM;
2831

2832
        if (symlink) {
5,499✔
2833
                s = strv_new(symlink);
6✔
2834
                if (!s)
6✔
2835
                        return -ENOMEM;
2836
        }
2837

2838
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,499✔
2839
                return -ENOMEM;
2840

2841
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,499✔
2842
                .path = TAKE_PTR(p),
5,499✔
2843
                .symlinks = TAKE_PTR(s),
5,499✔
2844
                .flags = flags,
2845
        };
2846

2847
        return 1; /* new item is added */
5,499✔
2848
}
2849

2850
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
890✔
2851
        assert(a);
890✔
2852
        assert(b);
890✔
2853

2854
        return path_compare(a->path, b->path);
890✔
2855
}
2856

2857
void exec_directory_sort(ExecDirectory *d) {
126,386✔
2858
        assert(d);
126,386✔
2859

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

2865
        if (d->n_items <= 1)
126,386✔
2866
                return;
2867

2868
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
160✔
2869

2870
        for (size_t i = 1; i < d->n_items; i++)
695✔
2871
                for (size_t j = 0; j < i; j++)
1,780✔
2872
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,260✔
2873
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2874
                                break;
15✔
2875
                        }
2876
}
2877

2878
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2879
        ExecDirectoryType t;
×
2880

2881
        assert(s);
×
2882

2883
        if (streq(s, "all"))
×
2884
                return EXEC_CLEAN_ALL;
2885
        if (streq(s, "fdstore"))
×
2886
                return EXEC_CLEAN_FDSTORE;
2887

2888
        t = exec_resource_type_from_string(s);
×
2889
        if (t < 0)
×
2890
                return (ExecCleanMask) t;
2891

2892
        return 1U << t;
×
2893
}
2894

2895
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2896
        [EXEC_INPUT_NULL]      = "null",
2897
        [EXEC_INPUT_TTY]       = "tty",
2898
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2899
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2900
        [EXEC_INPUT_SOCKET]    = "socket",
2901
        [EXEC_INPUT_NAMED_FD]  = "fd",
2902
        [EXEC_INPUT_DATA]      = "data",
2903
        [EXEC_INPUT_FILE]      = "file",
2904
};
2905

2906
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
15,767✔
2907

2908
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2909
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2910
        [EXEC_OUTPUT_NULL]                = "null",
2911
        [EXEC_OUTPUT_TTY]                 = "tty",
2912
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2913
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2914
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2915
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2916
        [EXEC_OUTPUT_SOCKET]              = "socket",
2917
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2918
        [EXEC_OUTPUT_FILE]                = "file",
2919
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2920
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2921
};
2922

2923
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
31,724✔
2924

2925
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2926
        [EXEC_UTMP_INIT]  = "init",
2927
        [EXEC_UTMP_LOGIN] = "login",
2928
        [EXEC_UTMP_USER]  = "user",
2929
};
2930

2931
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,011✔
2932

2933
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2934
        [EXEC_PRESERVE_NO]      = "no",
2935
        [EXEC_PRESERVE_YES]     = "yes",
2936
        [EXEC_PRESERVE_RESTART] = "restart",
2937
};
2938

2939
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,008✔
2940

2941
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
2942
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2943
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
2944
        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
2945
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
2946
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
2947
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
2948
};
2949

2950
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
138,238✔
2951

2952
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
2953
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2954
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
2955
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
2956
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
2957
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
2958
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
2959
};
2960

2961
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2962

2963
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2964
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
2965
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
2966
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
2967
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
2968
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
2969
};
2970

2971
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2972

2973
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
2974
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
2975
 * directories, specifically .timer units with their timestamp touch file. */
2976
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2977
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
2978
        [EXEC_DIRECTORY_STATE]         = "state",
2979
        [EXEC_DIRECTORY_CACHE]         = "cache",
2980
        [EXEC_DIRECTORY_LOGS]          = "logs",
2981
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
2982
};
2983

2984
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
263✔
2985

2986
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
2987
        [EXEC_KEYRING_INHERIT] = "inherit",
2988
        [EXEC_KEYRING_PRIVATE] = "private",
2989
        [EXEC_KEYRING_SHARED]  = "shared",
2990
};
2991

2992
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
15,374✔
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