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

systemd / systemd / 25026908423

27 Apr 2026 07:14PM UTC coverage: 71.865% (-0.3%) from 72.175%
25026908423

push

github

daandemeyer
udev: don't assert on worker cap after killing a broken idle worker

manager_can_process_event() considers an event processable if either
there is room below children_max to spawn, or an idle worker exists.
When only the latter holds, event_run() picks the idle worker and
tries device_monitor_send(). If that send fails, event_run() SIGKILLs
the worker, marks it WORKER_KILLED and continues the loop. With no
other idle worker available, it falls through to worker_spawn(),
guarded by:

    assert(hashmap_size(manager->workers) < manager->config.children_max);

The just-killed worker is still in manager->workers until its SIGCHLD
is reaped by on_worker_exit(), so at the cap this assertion trips and
udevd aborts:

    Assertion 'hashmap_size(manager->workers) < manager->config.children_max'
    failed at src/udev/udev-manager.c:635, function event_run(). Aborting.

Instead of asserting, bail out when we are already at the worker
limit. The event remains in EVENT_QUEUED; once the killed worker's
SIGCHLD arrives and frees it from the hashmap, on_post() re-runs
event_queue_start() and the event is retried.

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

7309 existing lines in 125 files now uncovered.

322519 of 448782 relevant lines covered (71.87%)

1173939.78 hits per line

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

74.01
/src/bootctl/bootctl.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/stat.h>
4

5
#include "sd-varlink.h"
6

7
#include "blockdev-util.h"
8
#include "boot-entry.h"
9
#include "bootctl.h"
10
#include "bootctl-cleanup.h"
11
#include "bootctl-install.h"
12
#include "bootctl-random-seed.h"
13
#include "bootctl-reboot-to-firmware.h"
14
#include "bootctl-set-efivar.h"
15
#include "bootctl-status.h"
16
#include "bootctl-uki.h"
17
#include "bootctl-unlink.h"
18
#include "bootctl-util.h"
19
#include "build.h"
20
#include "crypto-util.h"
21
#include "devnum-util.h"
22
#include "dissect-image.h"
23
#include "efi-loader.h"
24
#include "efivars.h"
25
#include "escape.h"
26
#include "fd-util.h"
27
#include "find-esp.h"
28
#include "format-table.h"
29
#include "image-policy.h"
30
#include "log.h"
31
#include "loop-util.h"
32
#include "main-func.h"
33
#include "mount-util.h"
34
#include "options.h"
35
#include "pager.h"
36
#include "parse-argument.h"
37
#include "path-util.h"
38
#include "pretty-print.h"
39
#include "string-table.h"
40
#include "string-util.h"
41
#include "strv.h"
42
#include "varlink-io.systemd.BootControl.h"
43
#include "varlink-util.h"
44
#include "verbs.h"
45
#include "virt.h"
46

47
static GracefulMode _arg_graceful = ARG_GRACEFUL_NO;
48

49
char *arg_esp_path = NULL;
50
char *arg_xbootldr_path = NULL;
51
bool arg_print_esp_path = false;
52
bool arg_print_dollar_boot_path = false;
53
bool arg_print_loader_path = false;
54
bool arg_print_stub_path = false;
55
bool arg_print_efi_architecture = false;
56
unsigned arg_print_root_device = 0;
57
int arg_touch_variables = -1;
58
bool arg_install_random_seed = true;
59
PagerFlags arg_pager_flags = 0;
60
bool arg_quiet = false;
61
int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */
62
sd_id128_t arg_machine_id = SD_ID128_NULL;
63
char *arg_install_layout = NULL;
64
BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
65
char *arg_entry_token = NULL;
66
sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
67
bool arg_arch_all = false;
68
char *arg_root = NULL;
69
char *arg_image = NULL;
70
InstallSource arg_install_source = INSTALL_SOURCE_AUTO;
71
char *arg_efi_boot_option_description = NULL;
72
bool arg_efi_boot_option_description_with_device = false;
73
bool arg_dry_run = false;
74
ImagePolicy *arg_image_policy = NULL;
75
bool arg_varlink = false;
76
bool arg_secure_boot_auto_enroll = false;
77
char *arg_certificate = NULL;
78
CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
79
char *arg_certificate_source = NULL;
80
char *arg_private_key = NULL;
81
KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
82
char *arg_private_key_source = NULL;
83

84
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
306✔
85
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
306✔
86
STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
306✔
87
STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
306✔
88
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
306✔
89
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
306✔
90
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
306✔
91
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
306✔
92
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
306✔
93
STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
306✔
94
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
306✔
95
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
306✔
96

97
static const char* const install_source_table[_INSTALL_SOURCE_MAX] = {
98
        [INSTALL_SOURCE_IMAGE] = "image",
99
        [INSTALL_SOURCE_HOST]  = "host",
100
        [INSTALL_SOURCE_AUTO]  = "auto",
101
};
102

103
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_source, InstallSource);
×
104

105
int acquire_esp(int unprivileged_mode,
261✔
106
                bool graceful,
107
                int *ret_fd,
108
                uint32_t *ret_part,
109
                uint64_t *ret_pstart,
110
                uint64_t *ret_psize,
111
                sd_id128_t *ret_uuid,
112
                dev_t *ret_devid) {
113

114
        _cleanup_free_ char *np = NULL;
261✔
115
        int r;
261✔
116

117
        /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on
118
         * its own, except for ENOKEY (which is good, we want to show our own message in that case,
119
         * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case
120
         * we simply eat up the error here, so that --list and --status work too, without noise about
121
         * this). */
122

123
        r = find_esp_and_warn_full(arg_root, arg_esp_path, unprivileged_mode, &np, ret_fd, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
261✔
124
        if (r == -ENOKEY) {
261✔
125
                if (graceful)
5✔
126
                        return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r,
×
127
                                              "Couldn't find EFI system partition, skipping.");
128

129
                return log_error_errno(r,
5✔
130
                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot/ or /efi/.\n"
131
                                       "Alternatively, use --esp-path= to specify path to mount point.");
132
        }
133
        if (r < 0)
256✔
134
                return r;
135

136
        free_and_replace(arg_esp_path, np);
255✔
137
        log_debug("Using EFI System Partition at %s.", arg_esp_path);
255✔
138

139
        return 1; /* for symmetry with acquire_xbootldr() below: found */
140
}
141

142
int acquire_xbootldr(
205✔
143
                int unprivileged_mode,
144
                int *ret_fd,
145
                sd_id128_t *ret_uuid,
146
                dev_t *ret_devid) {
147

148
        int r;
205✔
149

150
        _cleanup_free_ char *np = NULL;
205✔
151
        _cleanup_close_ int fd = -EBADF;
205✔
152
        r = find_xbootldr_and_warn_full(
245✔
153
                        arg_root,
154
                        arg_xbootldr_path,
155
                        unprivileged_mode,
156
                        &np,
157
                        ret_fd ? &fd : NULL,
158
                        ret_uuid,
159
                        ret_devid);
160
        if (r == -ENOKEY || (r >= 0 && arg_esp_path && path_equal(np, arg_esp_path))) {
205✔
161

162
                if (arg_esp_path)
158✔
163
                        log_debug("Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
157✔
164
                else
165
                        log_debug("Found neither an XBOOTLDR partition, nor an ESP.");
1✔
166

167
                arg_xbootldr_path = mfree(arg_xbootldr_path);
158✔
168

169
                if (ret_fd)
158✔
170
                        *ret_fd = -EBADF;
137✔
171
                if (ret_uuid)
158✔
172
                        *ret_uuid = SD_ID128_NULL;
16✔
173
                if (ret_devid)
158✔
174
                        *ret_devid = 0;
21✔
175

176
                return 0; /* not found */
158✔
177
        }
178
        if (r < 0)
46✔
179
                return r;
180

181
        free_and_replace(arg_xbootldr_path, np);
46✔
182
        log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
46✔
183

184
        if (ret_fd)
46✔
185
                *ret_fd = TAKE_FD(fd);
28✔
186

187
        return 1; /* found */
188
}
189

190
static int print_loader_or_stub_path(void) {
8✔
191
        _cleanup_free_ char *p = NULL;
8✔
192
        sd_id128_t uuid;
8✔
193
        int r;
8✔
194

195
        if (arg_print_loader_path) {
8✔
196
                r = efi_loader_get_device_part_uuid(&uuid);
1✔
197
                if (r == -ENOENT)
1✔
198
                        return log_error_errno(r, "No loader partition UUID passed.");
×
199
                if (r < 0)
1✔
200
                        return log_error_errno(r, "Unable to determine loader partition UUID: %m");
×
201

202
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &p);
1✔
203
                if (r == -ENOENT)
1✔
204
                        return log_error_errno(r, "No loader EFI binary path passed.");
×
205
                if (r < 0)
1✔
206
                        return log_error_errno(r, "Unable to determine loader EFI binary path: %m");
×
207
        } else {
208
                assert(arg_print_stub_path);
7✔
209

210
                r = efi_stub_get_device_part_uuid(&uuid);
7✔
211
                if (r == -ENOENT)
7✔
212
                        return log_error_errno(r, "No stub partition UUID passed.");
×
213
                if (r < 0)
7✔
214
                        return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
215

216
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &p);
7✔
217
                if (r == -ENOENT)
7✔
218
                        return log_error_errno(r, "No stub EFI binary path passed.");
×
219
                if (r < 0)
7✔
220
                        return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
221
        }
222

223
        sd_id128_t esp_uuid;
8✔
224
        r = acquire_esp(/* unprivileged_mode= */ false,
8✔
225
                        /* graceful= */ false,
226
                        /* ret_fd= */ NULL,
227
                        /* ret_part= */ NULL,
228
                        /* ret_pstart= */ NULL,
229
                        /* ret_psize= */ NULL,
230
                        &esp_uuid,
231
                        /* ret_devid= */ NULL);
232
        if (r < 0)
8✔
233
                return r;
234

235
        const char *found_path = NULL;
8✔
236
        if (sd_id128_equal(esp_uuid, uuid))
8✔
237
                found_path = arg_esp_path;
8✔
238
        else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
×
239
                sd_id128_t xbootldr_uuid;
×
240

241
                r = acquire_xbootldr(/* unprivileged_mode= */ false,
×
242
                                     /* ret_fd= */ NULL,
243
                                     &xbootldr_uuid,
244
                                     /* ret_devid= */ NULL);
245
                if (r < 0)
×
246
                        return r;
×
247

248
                if (sd_id128_equal(xbootldr_uuid, uuid))
×
249
                        found_path = arg_xbootldr_path;
×
250
        }
251

252
        if (!found_path)
8✔
253
                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to discover partition " SD_ID128_FORMAT_STR " among mounted boot partitions.", SD_ID128_FORMAT_VAL(uuid));
×
254

255
        _cleanup_free_ char *j = path_join(found_path, p);
16✔
256
        if (!j)
8✔
257
                return log_oom();
×
258

259
        puts(j);
8✔
260
        return 0;
261
}
262

263
GracefulMode arg_graceful(void) {
290✔
264
        static bool chroot_checked = false;
290✔
265

266
        if (!chroot_checked && running_in_chroot() > 0) {
290✔
267
                if (_arg_graceful == ARG_GRACEFUL_NO)
×
268
                        log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Running in a chroot, enabling --graceful.");
×
269

270
                _arg_graceful = ARG_GRACEFUL_FORCE;
×
271
        }
272

273
        chroot_checked = true;
290✔
274

275
        return _arg_graceful;
290✔
276
}
277

278
static int help(void) {
3✔
279
        _cleanup_free_ char *link = NULL;
3✔
280
        int r;
3✔
281

282
        pager_open(arg_pager_flags);
3✔
283

284
        r = terminal_urlify_man("bootctl", "1", &link);
3✔
285
        if (r < 0)
3✔
286
                return log_oom();
×
287

288
        static const char *const verb_groups[] = {
3✔
289
                "Generic EFI Firmware/Boot Loader Commands",
290
                "Boot Loader Specification Commands",
291
                "Boot Loader Interface Commands",
292
                "systemd-boot Commands",
293
                "Kernel Image Commands",
294
        };
295

296
        static const char *const option_groups[] = {
3✔
297
                "Block Device Discovery Commands",
298
                "Options",
299
        };
300

301
        _cleanup_(table_unref_many) Table *verb_tables[ELEMENTSOF(verb_groups) + 1] = {};
3✔
302
        _cleanup_(table_unref_many) Table *option_tables[ELEMENTSOF(option_groups) + 1] = {};
3✔
303

304
        for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
18✔
305
                r = verbs_get_help_table_group(verb_groups[i], &verb_tables[i]);
15✔
306
                if (r < 0)
15✔
307
                        return r;
308
        }
309

310
        for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
9✔
311
                r = option_parser_get_help_table_group(option_groups[i], &option_tables[i]);
6✔
312
                if (r < 0)
6✔
313
                        return r;
314
        }
315

316
        (void) table_sync_column_widths(0,
3✔
317
                                        verb_tables[0], verb_tables[1], verb_tables[2],
318
                                        verb_tables[3], verb_tables[4],
319
                                        option_tables[0], option_tables[1]);
320

321
        printf("%s [OPTIONS...] COMMAND ...\n"
6✔
322
               "\n%sControl EFI firmware boot settings and manage boot loader.%s\n",
323
               program_invocation_short_name,
324
               ansi_highlight(),
325
               ansi_normal());
326

327
        for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
18✔
328
                printf("\n%s%s:%s\n", ansi_underline(), verb_groups[i], ansi_normal());
45✔
329

330
                r = table_print_or_warn(verb_tables[i]);
15✔
331
                if (r < 0)
15✔
332
                        return r;
333
        }
334

335
        for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
9✔
336
                printf("\n%s%s:%s\n", ansi_underline(), option_groups[i], ansi_normal());
18✔
337

338
                r = table_print_or_warn(option_tables[i]);
6✔
339
                if (r < 0)
6✔
340
                        return r;
341
        }
342

343
        printf("\nSee the %s for details.\n", link);
3✔
344
        return 0;
345
}
346

347
VERB_COMMON_HELP(help);
×
348

349
VERB_GROUP("Generic EFI Firmware/Boot Loader Commands");
350

351
VERB_SCOPE(, verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT,
352
           "Show status of installed boot loader and EFI variables");
353

354
VERB_SCOPE(, verb_reboot_to_firmware, "reboot-to-firmware", "[BOOL]", VERB_ANY, 2, 0,
355
           "Query or set reboot-to-firmware EFI flag");
356

357
VERB_GROUP("Boot Loader Specification Commands");
358

359
VERB_SCOPE_NOARG(, verb_list, "list",
360
           "List boot loader entries");
361

362
VERB_SCOPE(, verb_unlink, "unlink", "ID", 2, 2, 0,
363
           "Remove boot loader entry");
364

365
VERB_SCOPE_NOARG(, verb_cleanup, "cleanup",
366
           "Remove files in ESP not referenced in any boot entry");
367

368
VERB_GROUP("Boot Loader Interface Commands");
369

370
VERB_SCOPE(, verb_set_efivar, "set-default", "ID", 2, 2, 0,
371
           "Set default boot loader entry");
372

373
VERB_SCOPE(, verb_set_efivar, "set-oneshot", "ID", 2, 2, 0,
374
           "Set default boot loader entry, for next boot only");
375

376
VERB_SCOPE(, verb_set_efivar, "set-sysfail", "ID", 2, 2, 0,
377
           "Set boot loader entry used in case of a system failure");
378

379
VERB_SCOPE(, verb_set_efivar, "set-timeout", "SECONDS", 2, 2, 0,
380
           "Set the menu timeout");
381

382
VERB_SCOPE(, verb_set_efivar, "set-timeout-oneshot", "SECONDS", 2, 2, 0,
383
           "Set the menu timeout for the next boot only");
384

385
VERB_SCOPE(, verb_set_efivar, "set-preferred", "ID", 2, 2, 0,
386
           /* help= */ NULL);
387

388
VERB_GROUP("systemd-boot Commands");
389

390
VERB_SCOPE(, verb_install, "install", NULL, VERB_ANY, 1, 0,
391
           "Install systemd-boot to the ESP and EFI variables");
392

393
VERB_SCOPE(, verb_install, "update", NULL, VERB_ANY, 1, 0,
394
           "Update systemd-boot in the ESP and EFI variables");
395

396
VERB_SCOPE_NOARG(, verb_remove, "remove",
397
           "Remove systemd-boot from the ESP and EFI variables");
398

399
VERB_SCOPE_NOARG(, verb_is_installed, "is-installed",
400
           "Test whether systemd-boot is installed in the ESP");
401

402
VERB_SCOPE_NOARG(, verb_random_seed, "random-seed",
403
           "Initialize or refresh random seed in ESP and EFI variables");
404

405
VERB_GROUP("Kernel Image Commands");
406

407
VERB_SCOPE(, verb_kernel_identify, "kernel-identify", "KERNEL-IMAGE", 2, 2, 0,
408
           "Identify kernel image type");
409

410
VERB_SCOPE(, verb_kernel_inspect, "kernel-inspect", "KERNEL-IMAGE", 2, 2, 0,
411
           "Prints details about the kernel image");
412

413
static int parse_argv(int argc, char *argv[], char ***ret_args) {
306✔
414
        int r;
306✔
415

416
        assert(argc >= 0);
306✔
417
        assert(argv);
306✔
418

419
        OptionParser opts = { argc, argv };
306✔
420

421
        FOREACH_OPTION(c, &opts, /* on_error= */ return c)
1,358✔
422
                switch (c) {
446✔
423

424
                OPTION_GROUP("Block Device Discovery Commands"): {}
48✔
425

426
                OPTION('p', "print-esp-path", NULL, "Print path to the EFI System Partition mount point"): {}
48✔
427
                OPTION_LONG("print-path", NULL, /* help= */ NULL):  /* Compatibility alias */
48✔
428
                        arg_print_esp_path = true;
48✔
429
                        break;
48✔
430

431
                OPTION('x', "print-boot-path", NULL, "Print path to the $BOOT partition mount point"):
9✔
432
                        arg_print_dollar_boot_path = true;
9✔
433
                        break;
9✔
434

435
                OPTION_LONG("print-loader-path", NULL, "Print path to currently booted boot loader binary"):
1✔
436
                        arg_print_loader_path = true;
1✔
437
                        break;
1✔
438

439
                OPTION_LONG("print-stub-path", NULL, "Print path to currently booted unified kernel binary"):
7✔
440
                        arg_print_stub_path = true;
7✔
441
                        break;
7✔
442

443
                OPTION('R', "print-root-device", NULL,
9✔
444
                       "Print path to the block device node backing the root file system"
445
                       " (returns e.g. /dev/nvme0n1p5)"): {}
9✔
446
                OPTION_HELP_VERBATIM("-RR",
9✔
447
                                     "Print path to the whole disk block device node backing the root FS"
448
                                     " (returns e.g. /dev/nvme0n1)"):
449
                        arg_print_root_device++;
9✔
450
                        break;
9✔
451

452
                OPTION_LONG("print-efi-architecture", NULL, "Print the local EFI architecture string"):
1✔
453
                        arg_print_efi_architecture = true;
1✔
454
                        break;
1✔
455

456
                OPTION_GROUP("Options"): {}
3✔
457

458
                OPTION_COMMON_HELP:
3✔
459
                        return help();
3✔
460

461
                OPTION_COMMON_VERSION:
3✔
462
                        return version();
3✔
463

464
                OPTION_LONG("esp-path", "PATH", "Path to the EFI System Partition (ESP)"): {}
3✔
465
                OPTION_LONG("path", "PATH", /* help= */ NULL):  /* Compatibility alias */
3✔
466
                        r = free_and_strdup(&arg_esp_path, opts.arg);
3✔
467
                        if (r < 0)
3✔
UNCOV
468
                                return log_oom();
×
469
                        break;
470

471
                OPTION_LONG("boot-path", "PATH", "Path to the $BOOT partition"):
3✔
472
                        r = free_and_strdup(&arg_xbootldr_path, opts.arg);
3✔
473
                        if (r < 0)
3✔
UNCOV
474
                                return log_oom();
×
475
                        break;
476

477
                OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
34✔
478
                        r = parse_path_argument(opts.arg, /* suppress_root= */ true, &arg_root);
34✔
479
                        if (r < 0)
34✔
480
                                return r;
481
                        break;
482

483
                OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
29✔
484
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &arg_image);
29✔
485
                        if (r < 0)
29✔
486
                                return r;
487
                        break;
488

UNCOV
489
                OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
×
490
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
491
                        if (r < 0)
×
492
                                return r;
493
                        break;
494

UNCOV
495
                OPTION_LONG("install-source", "SOURCE",
×
496
                            "Where to pick files when using --root=/--image= (auto, image, host)"): {
UNCOV
497
                        InstallSource is = install_source_from_string(opts.arg);
×
498
                        if (is < 0)
×
499
                                return log_error_errno(is, "Unexpected parameter for --install-source=: %s", opts.arg);
×
500

UNCOV
501
                        arg_install_source = is;
×
502
                        break;
×
503
                }
504

505
                OPTION_LONG("variables", "BOOL", "Whether to modify EFI variables"):
119✔
506
                        r = parse_tristate_argument_with_auto("--variables=", opts.arg, &arg_touch_variables);
119✔
507
                        if (r < 0)
119✔
508
                                return r;
509
#if !ENABLE_EFI
510
                        if (arg_touch_variables > 0)
511
                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
512
                                                       "Compiled without support for EFI, --variables=%s cannot be specified.", opts.arg);
513
#endif
514
                        break;
515

UNCOV
516
                OPTION_LONG("no-variables", NULL, /* help= */ NULL):  /* Compatibility alias */
×
517
                        arg_touch_variables = false;
×
518
                        break;
×
519

UNCOV
520
                OPTION_LONG("random-seed", "BOOL", "Whether to create random-seed file during install"):
×
521
                        r = parse_boolean_argument("--random-seed=", opts.arg, &arg_install_random_seed);
×
522
                        if (r < 0)
×
523
                                return r;
524
                        break;
525

UNCOV
526
                OPTION_COMMON_NO_PAGER:
×
527
                        arg_pager_flags |= PAGER_DISABLE;
×
528
                        break;
×
529

530
                OPTION_LONG("graceful", NULL,
137✔
531
                            "Don't fail when the ESP cannot be found or EFI variables cannot be written"):
532
                        _arg_graceful = ARG_GRACEFUL_YES;
137✔
533
                        break;
137✔
534

535
                OPTION('q', "quiet", NULL, "Suppress output"):
6✔
536
                        arg_quiet = true;
6✔
537
                        break;
6✔
538

UNCOV
539
                OPTION_LONG("entry-token", "TOKEN",
×
540
                            "Entry token to use for this installation"
541
                            " (machine-id, os-id, os-image-id, auto, literal:…)"):
UNCOV
542
                        r = parse_boot_entry_token_type(opts.arg, &arg_entry_token_type, &arg_entry_token);
×
543
                        if (r < 0)
×
544
                                return r;
545
                        break;
546

547
                OPTION_LONG("make-entry-directory", "yes|no|auto", "Create $BOOT/ENTRY-TOKEN/ directory"): {}
13✔
548
                OPTION_LONG("make-machine-id-directory", "BOOL", /* help= */ NULL):  /* Compatibility alias */
13✔
549
                        if (streq(opts.arg, "auto"))  /* retained for backwards compatibility */
13✔
UNCOV
550
                                arg_make_entry_directory = -1; /* yes if machine-id is permanent */
×
551
                        else {
552
                                r = parse_boolean_argument("--make-entry-directory=", opts.arg, NULL);
13✔
553
                                if (r < 0)
13✔
554
                                        return r;
555

556
                                arg_make_entry_directory = r;
13✔
557
                        }
558
                        break;
559

560
                OPTION_COMMON_JSON:
6✔
561
                        r = parse_json_argument(opts.arg, &arg_json_format_flags);
6✔
562
                        if (r <= 0)
6✔
563
                                return r;
564
                        break;
565

566
                OPTION_LONG("all-architectures", NULL, "Install all supported EFI architectures"):
12✔
567
                        arg_arch_all = true;
12✔
568
                        break;
12✔
569

UNCOV
570
                OPTION_LONG("efi-boot-option-description", "DESCRIPTION",
×
571
                            "Description of the entry in the boot option list"):
UNCOV
572
                        if (!string_is_safe(opts.arg, STRING_ALLOW_BACKSLASHES|STRING_ALLOW_QUOTES|STRING_ALLOW_GLOBS)) {
×
573
                                _cleanup_free_ char *escaped = cescape(opts.arg);
×
574
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
575
                                                       "Invalid --efi-boot-option-description=: %s", strna(escaped));
576
                        }
UNCOV
577
                        if (strlen(opts.arg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
×
578
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
579
                                                       "--efi-boot-option-description= too long: %zu > %zu",
580
                                                       strlen(opts.arg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
UNCOV
581
                        r = free_and_strdup_warn(&arg_efi_boot_option_description, opts.arg);
×
582
                        if (r < 0)
×
583
                                return r;
584
                        break;
585

UNCOV
586
                OPTION_LONG("efi-boot-option-description-with-device", "BOOL",
×
587
                            "Suffix description with disk vendor/model/serial"):
UNCOV
588
                        r = parse_boolean_argument("--efi-boot-option-description-with-device=", opts.arg,
×
589
                                                   &arg_efi_boot_option_description_with_device);
UNCOV
590
                        if (r < 0)
×
591
                                return r;
592
                        break;
593

UNCOV
594
                OPTION_LONG("dry-run", NULL, "Dry run (unlink and cleanup)"):
×
595
                        arg_dry_run = true;
×
596
                        break;
×
597

598
                OPTION_LONG("secure-boot-auto-enroll", "BOOL", "Set up secure boot auto-enrollment"):
1✔
599
                        r = parse_boolean_argument("--secure-boot-auto-enroll=", opts.arg,
1✔
600
                                                   &arg_secure_boot_auto_enroll);
601
                        if (r < 0)
1✔
602
                                return r;
603
                        break;
604

605
                OPTION_COMMON_PRIVATE_KEY("Private key for Secure Boot auto-enrollment"):
1✔
606
                        r = free_and_strdup_warn(&arg_private_key, opts.arg);
1✔
607
                        if (r < 0)
1✔
608
                                return r;
609
                        break;
610

UNCOV
611
                OPTION_COMMON_PRIVATE_KEY_SOURCE:
×
612
                        r = parse_openssl_key_source_argument(opts.arg,
×
613
                                                              &arg_private_key_source,
614
                                                              &arg_private_key_source_type);
UNCOV
615
                        if (r < 0)
×
616
                                return r;
617
                        break;
618

619
                OPTION_COMMON_CERTIFICATE("PEM certificate to use when setting up Secure Boot auto-enrollment"):
1✔
620
                        r = free_and_strdup_warn(&arg_certificate, opts.arg);
1✔
621
                        if (r < 0)
1✔
622
                                return r;
623
                        break;
624

UNCOV
625
                OPTION_COMMON_CERTIFICATE_SOURCE:
×
626
                        r = parse_openssl_certificate_source_argument(opts.arg,
×
627
                                                                      &arg_certificate_source,
628
                                                                      &arg_certificate_source_type);
UNCOV
629
                        if (r < 0)
×
630
                                return r;
631
                        break;
632
                }
633

634
        char **args = option_parser_get_args(&opts);
300✔
635

636
        if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path + arg_print_efi_architecture > 1)
300✔
UNCOV
637
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
638
                                                       "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path, --print-efi-architecture cannot be combined.");
639

640
        if ((arg_root || arg_image) && args[0] && !STR_IN_SET(args[0], "status", "list",
300✔
641
                        "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
UNCOV
642
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
643
                                       "Options --root= and --image= are not supported with verb %s.",
644
                                       args[0]);
645

646
        if (arg_root && arg_image)
300✔
UNCOV
647
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
×
648

649
        if (arg_install_source != INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
300✔
UNCOV
650
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
×
651

652
        if (arg_dry_run && args[0] && !STR_IN_SET(args[0], "unlink", "cleanup"))
300✔
UNCOV
653
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry-run is only supported with --unlink or --cleanup");
×
654

655
        if (arg_secure_boot_auto_enroll) {
300✔
656
#if HAVE_OPENSSL
657
                if (!arg_certificate)
1✔
UNCOV
658
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided.");
×
659

660
                if (!arg_private_key)
1✔
UNCOV
661
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided.");
×
662
#else
663
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Secure boot auto-enrollment requested but OpenSSL support is disabled.");
664
#endif
665
        }
666

667
        r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
300✔
668
        if (r < 0)
300✔
UNCOV
669
                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
×
670
        if (r > 0) {
300✔
671
                arg_varlink = true;
16✔
672
                arg_pager_flags |= PAGER_DISABLE;
16✔
673
        }
674

675
        *ret_args = args;
300✔
676
        return 1;
300✔
677
}
678

679
static int vl_server(void) {
16✔
680
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
16✔
681
        int r;
16✔
682

683
        /* Invocation as Varlink service */
684

685
        r = varlink_server_new(
16✔
686
                        &varlink_server,
687
                        SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
688
                        /* userdata= */ NULL);
689
        if (r < 0)
16✔
UNCOV
690
                return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
691

692
        r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl);
16✔
693
        if (r < 0)
16✔
UNCOV
694
                return log_error_errno(r, "Failed to add Varlink interface: %m");
×
695

696
        r = sd_varlink_server_bind_method_many(
16✔
697
                        varlink_server,
698
                        "io.systemd.BootControl.ListBootEntries",     vl_method_list_boot_entries,
699
                        "io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
700
                        "io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware,
701
                        "io.systemd.BootControl.Install",             vl_method_install);
702
        if (r < 0)
16✔
UNCOV
703
                return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
704

705
        r = sd_varlink_server_loop_auto(varlink_server);
16✔
706
        if (r < 0)
16✔
UNCOV
707
                return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
708

709
        return 0;
710
}
711

712
static int run(int argc, char *argv[]) {
306✔
713
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
306✔
714
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
306✔
715
        int r;
306✔
716

717
        log_setup();
306✔
718

719
        char **args = NULL;
306✔
720
        r = parse_argv(argc, argv, &args);
306✔
721
        if (r <= 0)
306✔
722
                return r;
723

724
        if (arg_varlink)
300✔
725
                return vl_server();
16✔
726

727
        if (arg_print_root_device > 0) {
284✔
728
                _cleanup_free_ char *path = NULL;
6✔
729
                dev_t devno;
6✔
730

731
                r = blockdev_get_root(LOG_ERR, &devno);
6✔
732
                if (r < 0)
6✔
733
                        return r;
734
                if (r == 0) {
6✔
UNCOV
735
                        log_error("Root file system not backed by a (single) whole block device.");
×
736
                        return 80; /* some recognizable error code */
×
737
                }
738

739
                if (arg_print_root_device > 1) {
6✔
740
                        r = block_get_whole_disk(devno, &devno);
3✔
741
                        if (r < 0)
3✔
UNCOV
742
                                log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m");
×
743
                }
744

745
                r = device_path_make_canonical(S_IFBLK, devno, &path);
6✔
746
                if (r < 0)
6✔
UNCOV
747
                        return log_error_errno(r,
×
748
                                               "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
749
                                               DEVNUM_FORMAT_VAL(devno));
750

751
                puts(path);
6✔
752
                return 0;
753
        }
754

755
        if (arg_print_loader_path || arg_print_stub_path)
278✔
756
                return print_loader_or_stub_path();
8✔
757

758
        if (arg_print_efi_architecture) {
270✔
759
                printf("%s\n", get_efi_arch());
1✔
760
                return 0;
761
        }
762

763
        /* Open up and mount the image */
764
        if (arg_image) {
269✔
765
                assert(!arg_root);
26✔
766

767
                r = mount_image_privately_interactively(
26✔
768
                                arg_image,
769
                                arg_image_policy,
770
                                DISSECT_IMAGE_GENERIC_ROOT |
771
                                DISSECT_IMAGE_USR_NO_ROOT |
772
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
773
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
774
                                &mounted_dir,
775
                                /* ret_dir_fd= */ NULL,
776
                                &loop_device);
777
                if (r < 0)
26✔
778
                        return r;
779

780
                arg_root = strdup(mounted_dir);
26✔
781
                if (!arg_root)
26✔
UNCOV
782
                        return log_oom();
×
783
        }
784

785
        return dispatch_verb_with_args(args, NULL);
269✔
786
}
787

788
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
306✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc