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

systemd / systemd / 15057632786

15 May 2025 09:01PM UTC coverage: 72.267% (+0.02%) from 72.244%
15057632786

push

github

bluca
man: document how to hook stuff into system wakeup

Fixes: #6364

298523 of 413084 relevant lines covered (72.27%)

738132.88 hits per line

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

76.59
/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) {
34,521✔
78
        return IN_SET(i,
34,521✔
79
                      EXEC_INPUT_TTY,
80
                      EXEC_INPUT_TTY_FORCE,
81
                      EXEC_INPUT_TTY_FAIL);
82
}
83

84
static bool is_terminal_output(ExecOutput o) {
67,980✔
85
        return IN_SET(o,
67,980✔
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,758✔
92
        assert(context);
14,758✔
93

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

97
        if (context->tty_path)
14,093✔
98
                return context->tty_path;
717✔
99

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

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

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

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

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

119
        if (!tty_path)
1,094✔
120
                tty_path = exec_context_tty_path(context);
424✔
121

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

126
        /* Fill in data from kernel command line if anything is unspecified */
127
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,094✔
128
                (void) proc_cmdline_tty_size(
1,062✔
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,317✔
138
            exec_context_shall_ansi_seq_reset(context) &&
418✔
139
            isatty_safe(input_fd)) {
195✔
140
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
195✔
141
                if (r < 0)
195✔
142
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
195✔
143
        }
144

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

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

152
        assert(context);
13,073✔
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,073✔
160

161
        if (p && p->stdout_fd >= 0 && isatty_safe(p->stdout_fd))
13,073✔
162
                fd = p->stdout_fd;
16✔
163
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
13,057✔
164
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
12,074✔
165
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
670✔
166
                if (fd < 0)
670✔
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();
686✔
175
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
686✔
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))
686✔
178
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
179
        else if (lock_fd < 0)
686✔
180
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
181

182
        if (context->tty_reset)
686✔
183
                (void) terminal_reset_defensive(
181✔
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));
178✔
187

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

192
        if (!sd_id128_is_null(invocation_id)) {
1,323✔
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)
686✔
210
                (void) terminal_vhangup_fd(fd);
173✔
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);
686✔
215

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

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

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

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

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

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

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

240
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
60,921✔
241
        assert(context);
60,921✔
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,921✔
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,921✔
254
}
255

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

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

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

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

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

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

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

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

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

285
        assert(context);
31,800✔
286

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

290
        if (!strv_isempty(context->read_write_paths) ||
31,782✔
291
            !strv_isempty(context->read_only_paths) ||
29,693✔
292
            !strv_isempty(context->inaccessible_paths) ||
29,686✔
293
            !strv_isempty(context->exec_paths) ||
29,661✔
294
            !strv_isempty(context->no_exec_paths))
29,661✔
295
                return true;
296

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

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

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

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

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

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

315
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
29,406✔
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,383✔
319
                return true;
320

321
        if (context->private_devices ||
27,903✔
322
            context->private_mounts > 0 ||
27,163✔
323
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
26,739✔
324
            context->protect_system != PROTECT_SYSTEM_NO ||
26,716✔
325
            context->protect_home != PROTECT_HOME_NO ||
26,716✔
326
            context->protect_kernel_tunables ||
26,716✔
327
            context->protect_kernel_modules ||
26,716✔
328
            context->protect_kernel_logs ||
50,087✔
329
            exec_needs_cgroup_mount(context) ||
25,042✔
330
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,024✔
331
            context->proc_subset != PROC_SUBSET_ALL ||
49,931✔
332
            exec_needs_ipc_namespace(context) ||
49,922✔
333
            exec_needs_pid_namespace(context))
24,961✔
334
                return true;
2,977✔
335

336
        if (context->root_directory) {
24,926✔
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,921✔
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,921✔
356
                return true;
357

358
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
149,182✔
359
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
129,229✔
360
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
4,962✔
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,898✔
367
        assert(context);
3,898✔
368
        assert(params);
3,898✔
369

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

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

376
        if (!context->root_directory && !context->root_image)
3,248✔
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,843✔
389
        assert(context);
11,843✔
390

391
        if (!context->dynamic_user)
11,843✔
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,431✔
404
                const ExecParameters *params,
405
                const CGroupContext *c,
406
                char **ret) {
407

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

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

414
        if (!params->cgroup_path)
14,431✔
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,431✔
427
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
705✔
428
                        subgroup = ".control";
429
                else
430
                        subgroup = c->delegate_subgroup;
671✔
431
        }
432

433
        if (subgroup)
671✔
434
                p = path_join(params->cgroup_path, subgroup);
705✔
435
        else
436
                p = strdup(params->cgroup_path);
13,726✔
437
        if (!p)
14,431✔
438
                return -ENOMEM;
439

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

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

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

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

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

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

460
        log_unit_struct(unit, LOG_DEBUG,
2,208✔
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,208✔
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,208✔
478
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
479
        _cleanup_fclose_ FILE *f = NULL;
2,208✔
480
        int r;
2,208✔
481

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

494
        LOG_CONTEXT_PUSH_UNIT(unit);
4,416✔
495

496
        r = exec_context_load_environment(unit, context, &params->files_env);
2,208✔
497
        if (r < 0)
2,208✔
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,208✔
503

504
        if (params->cgroup_path) {
2,208✔
505
                r = exec_params_get_cgroup_path(params, cgroup_context, &subcgroup_path);
2,208✔
506
                if (r < 0)
2,208✔
507
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
508
                if (r > 0) {
2,208✔
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);
49✔
513
                        if (r < 0)
49✔
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,208✔
526
        if (r < 0)
2,208✔
527
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
528

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

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

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

541
        r = fd_cloexec(fileno(f), false);
2,208✔
542
        if (r < 0)
2,208✔
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,208✔
546
        if (r < 0)
2,208✔
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,208✔
553
        if (r < 0)
2,208✔
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,208✔
557
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,208✔
558

559
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,208✔
560
        dual_timestamp start_timestamp;
2,208✔
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,208✔
565
        if (r < 0)
2,208✔
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,208✔
571

572
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
573
        r = posix_spawn_wrapper(
2,208✔
574
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,208✔
575
                        STRV_MAKE(unit->manager->executor_path,
2,208✔
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,208✔
585

586
        if (r == -EUCLEAN && subcgroup_path)
2,208✔
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,208✔
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,208✔
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,208✔
601
                       command->path, pidref.pid, r > 0 ? "via" : "without");
602

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

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

609
void exec_context_init(ExecContext *c) {
58,497✔
610
        assert(c);
58,497✔
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) {
58,497✔
616
                .umask = 0022,
617
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
58,497✔
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
                .private_var_tmp = _PRIVATE_TMP_INVALID,
639
                .set_login_environment = -1,
640
        };
641

642
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
350,982✔
643
                d->mode = 0755;
292,485✔
644

645
        numa_policy_reset(&c->numa_policy);
58,497✔
646

647
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
58,497✔
648
}
58,497✔
649

650
void exec_context_done(ExecContext *c) {
46,969✔
651
        assert(c);
46,969✔
652

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

658
        rlimit_free_all(c->rlimit);
46,969✔
659

660
        for (size_t l = 0; l < 3; l++) {
187,876✔
661
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
140,907✔
662
                c->stdio_file[l] = mfree(c->stdio_file[l]);
140,907✔
663
        }
664

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

683
        c->supplementary_groups = strv_free(c->supplementary_groups);
46,969✔
684

685
        c->pam_name = mfree(c->pam_name);
46,969✔
686

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

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

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

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

710
        c->restrict_filesystems = set_free(c->restrict_filesystems);
46,969✔
711

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

717
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
281,814✔
718
                exec_directory_done(d);
234,845✔
719

720
        c->log_level_max = -1;
46,969✔
721

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

726
        c->log_ratelimit = (RateLimit) {};
46,969✔
727

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

731
        c->network_namespace_path = mfree(c->network_namespace_path);
46,969✔
732
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
46,969✔
733

734
        c->log_namespace = mfree(c->log_namespace);
46,969✔
735

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

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

744
        c->private_hostname = mfree(c->private_hostname);
46,969✔
745
}
46,969✔
746

747
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,011✔
748
        assert(c);
5,011✔
749

750
        if (!runtime_prefix)
5,011✔
751
                return 0;
752

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

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

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

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

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

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

781
        return 0;
782
}
783

784
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,040✔
785
        _cleanup_free_ char *p = NULL;
11,040✔
786

787
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,040✔
788
                return 0;
789

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

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

798
        return 0;
799
}
800

801
void exec_command_done(ExecCommand *c) {
100,003✔
802
        assert(c);
100,003✔
803

804
        c->path = mfree(c->path);
100,003✔
805
        c->argv = strv_free(c->argv);
100,003✔
806
}
100,003✔
807

808
void exec_command_done_array(ExecCommand *c, size_t n) {
27,791✔
809
        FOREACH_ARRAY(i, c, n)
111,162✔
810
                exec_command_done(i);
83,371✔
811
}
27,791✔
812

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

817
        exec_command_done(c);
16,604✔
818
        return mfree(c);
16,604✔
819
}
820

821
ExecCommand* exec_command_free_list(ExecCommand *c) {
123,333✔
822
        ExecCommand *i;
123,333✔
823

824
        while ((i = LIST_POP(command, c)))
139,937✔
825
                exec_command_free(i);
16,604✔
826

827
        return NULL;
123,333✔
828
}
829

830
void exec_command_free_array(ExecCommand **c, size_t n) {
19,150✔
831
        FOREACH_ARRAY(i, c, n)
142,468✔
832
                *i = exec_command_free_list(*i);
123,318✔
833
}
19,150✔
834

835
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
6,977✔
836
        FOREACH_ARRAY(i, c, n)
27,907✔
837
                exec_status_reset(&i->exec_status);
20,930✔
838
}
6,977✔
839

840
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,260✔
841
        FOREACH_ARRAY(i, c, n)
29,414✔
842
                LIST_FOREACH(command, z, *i)
27,337✔
843
                        exec_status_reset(&z->exec_status);
2,183✔
844
}
4,260✔
845

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

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

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

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

860
        switch (fd_index) {
38,520✔
861

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

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

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

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

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

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

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

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

889
        assert(c);
2,208✔
890
        assert(ret);
2,208✔
891

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

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

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

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

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

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

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

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

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

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

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

951
        *ret = TAKE_PTR(v);
2,208✔
952

953
        return 0;
2,208✔
954
}
955

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

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

962
        tty = skip_dev_prefix(tty);
367✔
963

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

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

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

975
static bool exec_context_may_touch_tty(const ExecContext *ec) {
22,389✔
976
        assert(ec);
22,389✔
977

978
        return ec->tty_reset ||
44,627✔
979
                ec->tty_vhangup ||
22,238✔
980
                ec->tty_vt_disallocate ||
22,238✔
981
                is_terminal_input(ec->std_input) ||
22,238✔
982
                is_terminal_output(ec->std_output) ||
44,535✔
983
                is_terminal_output(ec->std_error);
21,973✔
984
}
985

986
bool exec_context_may_touch_console(const ExecContext *ec) {
20,872✔
987

988
        return exec_context_may_touch_tty(ec) &&
21,239✔
989
               tty_may_match_dev_console(exec_context_tty_path(ec));
367✔
990
}
991

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

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

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

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

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

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

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

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

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

1031
        prefix = strempty(prefix);
×
1032

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

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

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

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

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

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

1077
        prefix = strempty(prefix);
224✔
1078

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1581
        return false;
×
1582
}
1583

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

1587
        assert(c);
2,566✔
1588

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

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

1596
        return ioprio_normalize(p);
2,548✔
1597
}
1598

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

1602
        /* Explicit setting wins */
1603
        if (c->mount_apivfs >= 0)
33,951✔
1604
                return c->mount_apivfs > 0;
110✔
1605

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

1610
        return false;
1611
}
1612

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

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

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

1624
        if (exec_context_get_effective_mount_apivfs(c))
28,425✔
1625
                return true;
1626

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

1631
        return false;
1632
}
1633

1634
void exec_context_free_log_extra_fields(ExecContext *c) {
46,971✔
1635
        assert(c);
46,971✔
1636

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

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

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

1650
        assert(c);
1,517✔
1651

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1748
        assert(c);
1,263✔
1749
        assert(ret);
1,263✔
1750

1751
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,578✔
1752
                if (c->directories[t].n_items > 0)
6,315✔
1753
                        mask |= 1U << t;
299✔
1754

1755
        *ret = mask;
1,263✔
1756
        return 0;
1,263✔
1757
}
1758

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

1762
        assert(c);
1,283✔
1763

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

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

1771
        return n;
943✔
1772
}
1773

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

1779
        assert(c);
1,283✔
1780

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

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

1793
        return n;
1,283✔
1794
}
1795

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

1799
        assert(c);
1,283✔
1800

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

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

1811
        return n;
1812
}
1813

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

1817
        assert(c);
1,283✔
1818

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

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

1826
        return n < 0 ? SCHED_OTHER : n;
1,283✔
1827
}
1828

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

1833
        assert(c);
1,283✔
1834

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

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

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

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

1848
        assert(c);
1,283✔
1849

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

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

1857
        return (uint64_t) MAX(r, 0);
1,283✔
1858
}
1859

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

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

1866
        return c->user || c->dynamic_user || c->pam_name;
19,592✔
1867
}
1868

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

1872
        assert(c);
1,283✔
1873

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

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

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

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

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

1907
        strv_sort(l);
1,283✔
1908
#endif
1909

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

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

1916
        assert(c);
1,283✔
1917

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

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

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

1931
        strv_sort(l);
1,283✔
1932
#endif
1933

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

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

1940
        assert(c);
1,283✔
1941

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

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

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

1955
        strv_sort(l);
1,283✔
1956
#endif
1957

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

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

1965
        assert(c);
1,283✔
1966

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

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

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

1978
        strv_sort(l);
1,283✔
1979

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

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

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

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

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

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

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

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

2013
        if (s->pid != pid)
2,040✔
2014
                *s = (ExecStatus) {
7✔
2015
                        .pid = pid,
2016
                };
2017

2018
        dual_timestamp_now(&s->exit_timestamp);
2,040✔
2019

2020
        s->code = code;
2,040✔
2021
        s->status = status;
2,040✔
2022

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

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

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

2037
        s->handoff_timestamp = *ts;
7,774✔
2038
}
7,774✔
2039

2040
void exec_status_reset(ExecStatus *s) {
25,040✔
2041
        assert(s);
25,040✔
2042

2043
        *s = (ExecStatus) {};
25,040✔
2044
}
25,040✔
2045

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

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

2053
        prefix = strempty(prefix);
10✔
2054

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

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

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

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

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

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

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

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

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

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

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

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

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

2122
        prefix = strempty(prefix);
91✔
2123

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

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

2131
        assert(l);
16,604✔
2132
        assert(e);
16,604✔
2133

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

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

2146
        assert(c);
173✔
2147
        assert(path);
173✔
2148

2149
        va_start(ap, path);
173✔
2150
        l = strv_new_ap(path, ap);
173✔
2151
        va_end(ap);
173✔
2152

2153
        if (!l)
173✔
2154
                return -ENOMEM;
173✔
2155

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

2162
        free_and_replace(c->path, p);
173✔
2163

2164
        return strv_free_and_replace(c->argv, l);
173✔
2165
}
2166

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

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

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

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

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

2186
        return 0;
2187
}
2188

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

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

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

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

2202
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
130,683✔
2203
        assert(rt);
130,683✔
2204

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

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

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

2219
        exec_shared_runtime_done(rt);
130,655✔
2220
        return mfree(rt);
130,655✔
2221
}
2222

2223
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
173✔
2224
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
133,785✔
2225

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

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

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

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

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

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

2246
        assert(ret);
130,655✔
2247

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

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

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

2262
        *ret = n;
130,655✔
2263
        return 0;
130,655✔
2264
}
2265

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

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

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

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

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

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

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

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

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

2305
        rt->manager = m;
172✔
2306

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

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

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

2324
        assert(m);
6,195✔
2325
        assert(c);
6,195✔
2326
        assert(id);
6,195✔
2327

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

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

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

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

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

2355
        return 1;
2356
}
2357

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

2362
        assert(m);
6,298✔
2363
        assert(id);
6,298✔
2364
        assert(ret);
6,298✔
2365

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2452
        return 0;
73✔
2453
}
2454

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

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

2465
        assert(u);
133,613✔
2466
        assert(key);
133,613✔
2467
        assert(value);
133,613✔
2468

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

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

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

2486
                rt = rt_create;
130,483✔
2487
        }
2488

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

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

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

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

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

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

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

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

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

2527
        return 1;
2528
}
2529

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2635
        assert(m);
1,454✔
2636

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

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

2643
                (void) exec_shared_runtime_free(rt);
×
2644
        }
2645
}
1,454✔
2646

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

2658
        assert(unit);
6,298✔
2659
        assert(context);
6,298✔
2660
        assert(ret);
6,298✔
2661

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

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

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

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

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

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

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

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

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

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

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

2709
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,071✔
2710
        if (!rt)
5,071✔
2711
                return NULL;
2712

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2781
        exec_params_shallow_clear(p);
28✔
2782
}
2783

2784
void exec_directory_done(ExecDirectory *d) {
234,845✔
2785
        if (!d)
234,845✔
2786
                return;
2787

2788
        FOREACH_ARRAY(i, d->items, d->n_items) {
236,653✔
2789
                free(i->path);
1,808✔
2790
                strv_free(i->symlinks);
1,808✔
2791
        }
2792

2793
        d->items = mfree(d->items);
234,845✔
2794
        d->n_items = 0;
234,845✔
2795
        d->mode = 0755;
234,845✔
2796
}
2797

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

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

2806
        return NULL;
2807
}
2808

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

2815
        assert(d);
5,526✔
2816
        assert(path);
5,526✔
2817

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

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

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

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

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

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

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

2848
        return 1; /* new item is added */
5,511✔
2849
}
2850

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

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

2858
void exec_directory_sort(ExecDirectory *d) {
128,806✔
2859
        assert(d);
128,806✔
2860

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

2866
        if (d->n_items <= 1)
128,806✔
2867
                return;
2868

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

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

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

2882
        assert(s);
×
2883

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

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

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

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

2907
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
15,856✔
2908

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

2924
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
31,906✔
2925

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

2932
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,100✔
2933

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

2940
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,116✔
2941

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

2951
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
138,898✔
2952

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

2962
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2963

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

2972
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2973

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

2985
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
305✔
2986

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

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