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

systemd / systemd / 25893429527

14 May 2026 09:08PM UTC coverage: 72.364% (-0.2%) from 72.584%
25893429527

push

github

bluca
ci: switch SUSE mkosi mirror to cdn.o.o

The cdn mirror is preferred by SUSE for clouds/CIs. There have been issues with some
mirrors, which fail to download from GHA quite often lately, so hopefully this will
make it reliable again.

328159 of 453485 relevant lines covered (72.36%)

1405869.02 hits per line

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

75.57
/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-link.h"
13
#include "bootctl-random-seed.h"
14
#include "bootctl-reboot-to-firmware.h"
15
#include "bootctl-set-efivar.h"
16
#include "bootctl-status.h"
17
#include "bootctl-uki.h"
18
#include "bootctl-unlink.h"
19
#include "bootctl-util.h"
20
#include "bootspec-util.h"
21
#include "build.h"
22
#include "crypto-util.h"
23
#include "devnum-util.h"
24
#include "dissect-image.h"
25
#include "efi-loader.h"
26
#include "efivars.h"
27
#include "escape.h"
28
#include "fd-util.h"
29
#include "find-esp.h"
30
#include "format-table.h"
31
#include "image-policy.h"
32
#include "log.h"
33
#include "loop-util.h"
34
#include "main-func.h"
35
#include "mount-util.h"
36
#include "options.h"
37
#include "pager.h"
38
#include "parse-argument.h"
39
#include "parse-util.h"
40
#include "path-util.h"
41
#include "pretty-print.h"
42
#include "string-table.h"
43
#include "string-util.h"
44
#include "strv.h"
45
#include "varlink-io.systemd.BootControl.h"
46
#include "varlink-util.h"
47
#include "verbs.h"
48
#include "virt.h"
49

50
static GracefulMode _arg_graceful = ARG_GRACEFUL_NO;
51

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

94
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
345✔
95
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
345✔
96
STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
345✔
97
STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
345✔
98
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
345✔
99
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
345✔
100
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
345✔
101
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
345✔
102
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
345✔
103
STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
345✔
104
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
345✔
105
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
345✔
106
STATIC_DESTRUCTOR_REGISTER(arg_entry_title, freep);
345✔
107
STATIC_DESTRUCTOR_REGISTER(arg_entry_version, freep);
345✔
108
STATIC_DESTRUCTOR_REGISTER(arg_extras, strv_freep);
345✔
109

110
static const char* const install_source_table[_INSTALL_SOURCE_MAX] = {
111
        [INSTALL_SOURCE_IMAGE] = "image",
112
        [INSTALL_SOURCE_HOST]  = "host",
113
        [INSTALL_SOURCE_AUTO]  = "auto",
114
};
115

116
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_source, InstallSource);
×
117

118
int acquire_esp(int unprivileged_mode,
295✔
119
                bool graceful,
120
                int *ret_fd,
121
                uint32_t *ret_part,
122
                uint64_t *ret_pstart,
123
                uint64_t *ret_psize,
124
                sd_id128_t *ret_uuid,
125
                dev_t *ret_devid) {
126

127
        _cleanup_free_ char *np = NULL;
295✔
128
        int r;
295✔
129

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

136
        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);
295✔
137
        if (r == -ENOKEY) {
295✔
138
                if (graceful)
5✔
139
                        return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r,
×
140
                                              "Couldn't find EFI system partition, skipping.");
141

142
                return log_error_errno(r,
5✔
143
                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot/ or /efi/.\n"
144
                                       "Alternatively, use --esp-path= to specify path to mount point.");
145
        }
146
        if (r < 0)
290✔
147
                return r;
148

149
        free_and_replace(arg_esp_path, np);
289✔
150
        log_debug("Using EFI System Partition at %s.", arg_esp_path);
289✔
151

152
        return 1; /* for symmetry with acquire_xbootldr() below: found */
153
}
154

155
int acquire_xbootldr(
226✔
156
                int unprivileged_mode,
157
                int *ret_fd,
158
                sd_id128_t *ret_uuid,
159
                dev_t *ret_devid) {
160

161
        int r;
226✔
162

163
        _cleanup_free_ char *np = NULL;
226✔
164
        _cleanup_close_ int fd = -EBADF;
226✔
165
        r = find_xbootldr_and_warn_full(
267✔
166
                        arg_root,
167
                        arg_xbootldr_path,
168
                        unprivileged_mode,
169
                        &np,
170
                        ret_fd ? &fd : NULL,
171
                        ret_uuid,
172
                        ret_devid);
173
        if (r == -ENOKEY || (r >= 0 && arg_esp_path && path_equal(np, arg_esp_path))) {
226✔
174

175
                if (arg_esp_path)
179✔
176
                        log_debug("Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
169✔
177
                else
178
                        log_debug("Found neither an XBOOTLDR partition, nor an ESP.");
10✔
179

180
                arg_xbootldr_path = mfree(arg_xbootldr_path);
179✔
181

182
                if (ret_fd)
179✔
183
                        *ret_fd = -EBADF;
157✔
184
                if (ret_uuid)
179✔
185
                        *ret_uuid = SD_ID128_NULL;
16✔
186
                if (ret_devid)
179✔
187
                        *ret_devid = 0;
30✔
188

189
                return 0; /* not found */
190
        }
191
        if (r < 0)
46✔
192
                return r;
193

194
        free_and_replace(arg_xbootldr_path, np);
46✔
195
        log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
46✔
196

197
        if (ret_fd)
46✔
198
                *ret_fd = TAKE_FD(fd);
28✔
199

200
        return 1; /* found */
201
}
202

203
static int print_loader_or_stub_path(void) {
8✔
204
        _cleanup_free_ char *p = NULL;
8✔
205
        sd_id128_t uuid;
8✔
206
        int r;
8✔
207

208
        if (arg_print_loader_path) {
8✔
209
                r = efi_loader_get_device_part_uuid(&uuid);
1✔
210
                if (r == -ENOENT)
1✔
211
                        return log_error_errno(r, "No loader partition UUID passed.");
×
212
                if (r < 0)
1✔
213
                        return log_error_errno(r, "Unable to determine loader partition UUID: %m");
×
214

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

223
                r = efi_stub_get_device_part_uuid(&uuid);
7✔
224
                if (r == -ENOENT)
7✔
225
                        return log_error_errno(r, "No stub partition UUID passed.");
×
226
                if (r < 0)
7✔
227
                        return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
228

229
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &p);
7✔
230
                if (r == -ENOENT)
7✔
231
                        return log_error_errno(r, "No stub EFI binary path passed.");
×
232
                if (r < 0)
7✔
233
                        return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
234
        }
235

236
        sd_id128_t esp_uuid;
8✔
237
        r = acquire_esp(/* unprivileged_mode= */ false,
8✔
238
                        /* graceful= */ false,
239
                        /* ret_fd= */ NULL,
240
                        /* ret_part= */ NULL,
241
                        /* ret_pstart= */ NULL,
242
                        /* ret_psize= */ NULL,
243
                        &esp_uuid,
244
                        /* ret_devid= */ NULL);
245
        if (r < 0)
8✔
246
                return r;
247

248
        const char *found_path = NULL;
8✔
249
        if (sd_id128_equal(esp_uuid, uuid))
8✔
250
                found_path = arg_esp_path;
8✔
251
        else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
×
252
                sd_id128_t xbootldr_uuid;
×
253

254
                r = acquire_xbootldr(/* unprivileged_mode= */ false,
×
255
                                     /* ret_fd= */ NULL,
256
                                     &xbootldr_uuid,
257
                                     /* ret_devid= */ NULL);
258
                if (r < 0)
×
259
                        return r;
×
260

261
                if (sd_id128_equal(xbootldr_uuid, uuid))
×
262
                        found_path = arg_xbootldr_path;
×
263
        }
264

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

268
        _cleanup_free_ char *j = path_join(found_path, p);
16✔
269
        if (!j)
8✔
270
                return log_oom();
×
271

272
        puts(j);
8✔
273
        return 0;
274
}
275

276
GracefulMode arg_graceful(void) {
295✔
277
        static bool chroot_checked = false;
295✔
278

279
        if (!chroot_checked && running_in_chroot() > 0) {
295✔
280
                if (_arg_graceful == ARG_GRACEFUL_NO)
×
281
                        log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Running in a chroot, enabling --graceful.");
×
282

283
                _arg_graceful = ARG_GRACEFUL_FORCE;
×
284
        }
285

286
        chroot_checked = true;
295✔
287

288
        return _arg_graceful;
295✔
289
}
290

291
static int help(void) {
3✔
292
        _cleanup_free_ char *link = NULL;
3✔
293
        int r;
3✔
294

295
        pager_open(arg_pager_flags);
3✔
296

297
        r = terminal_urlify_man("bootctl", "1", &link);
3✔
298
        if (r < 0)
3✔
299
                return log_oom();
×
300

301
        static const char *const verb_groups[] = {
3✔
302
                "Generic EFI Firmware/Boot Loader Commands",
303
                "Boot Loader Specification Commands",
304
                "Boot Loader Interface Commands",
305
                "systemd-boot Commands",
306
                "Kernel Image Commands",
307
        };
308

309
        static const char *const option_groups[] = {
3✔
310
                "Block Device Discovery Commands",
311
                "Options",
312
        };
313

314
        Table *verb_tables[ELEMENTSOF(verb_groups)] = {};
3✔
315
        CLEANUP_ELEMENTS(verb_tables, table_unref_array_clear);
3✔
316
        Table *option_tables[ELEMENTSOF(option_groups)] = {};
3✔
317
        CLEANUP_ELEMENTS(option_tables, table_unref_array_clear);
3✔
318

319
        for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
18✔
320
                r = verbs_get_help_table_group(verb_groups[i], &verb_tables[i]);
15✔
321
                if (r < 0)
15✔
322
                        return r;
323
        }
324

325
        for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
9✔
326
                r = option_parser_get_help_table_group(option_groups[i], &option_tables[i]);
6✔
327
                if (r < 0)
6✔
328
                        return r;
329
        }
330

331
        (void) table_sync_column_widths(0,
3✔
332
                                        verb_tables[0], verb_tables[1], verb_tables[2],
333
                                        verb_tables[3], verb_tables[4],
334
                                        option_tables[0], option_tables[1]);
335

336
        printf("%s [OPTIONS...] COMMAND ...\n"
6✔
337
               "\n%sControl EFI firmware boot settings and manage boot loader.%s\n",
338
               program_invocation_short_name,
339
               ansi_highlight(),
340
               ansi_normal());
341

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

345
                r = table_print_or_warn(verb_tables[i]);
15✔
346
                if (r < 0)
15✔
347
                        return r;
348
        }
349

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

353
                r = table_print_or_warn(option_tables[i]);
6✔
354
                if (r < 0)
6✔
355
                        return r;
356
        }
357

358
        printf("\nSee the %s for details.\n", link);
3✔
359
        return 0;
360
}
361

362
VERB_COMMON_HELP(help);
×
363

364
VERB_GROUP("Generic EFI Firmware/Boot Loader Commands");
365

366
VERB_SCOPE(, verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT,
367
           "Show status of installed boot loader and EFI variables");
368

369
VERB_SCOPE(, verb_reboot_to_firmware, "reboot-to-firmware", "[BOOL]", VERB_ANY, 2, 0,
370
           "Query or set reboot-to-firmware EFI flag");
371

372
VERB_GROUP("Boot Loader Specification Commands");
373

374
VERB_SCOPE_NOARG(, verb_list, "list",
375
           "List boot loader entries");
376

377
VERB_SCOPE(, verb_unlink, "unlink", "ID", VERB_ANY, 2, 0,
378
           "Remove boot loader entry");
379

380
VERB_SCOPE(, verb_link, "link", "KERNEL", 2, 2, 0,
381
           "Create boot loader entry for specified kernel");
382

383
VERB_SCOPE_NOARG(, verb_cleanup, "cleanup",
384
           "Remove files in ESP not referenced in any boot entry");
385

386
VERB_GROUP("Boot Loader Interface Commands");
387

388
VERB_SCOPE(, verb_set_efivar, "set-default", "ID", 2, 2, 0,
389
           "Set default boot loader entry");
390

391
VERB_SCOPE(, verb_set_efivar, "set-oneshot", "ID", 2, 2, 0,
392
           "Set default boot loader entry, for next boot only");
393

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

397
VERB_SCOPE(, verb_set_efivar, "set-timeout", "SECONDS", 2, 2, 0,
398
           "Set the menu timeout");
399

400
VERB_SCOPE(, verb_set_efivar, "set-timeout-oneshot", "SECONDS", 2, 2, 0,
401
           "Set the menu timeout for the next boot only");
402

403
VERB_SCOPE(, verb_set_efivar, "set-preferred", "ID", 2, 2, 0,
404
           /* help= */ NULL);
405

406
VERB_GROUP("systemd-boot Commands");
407

408
VERB_SCOPE(, verb_install, "install", NULL, VERB_ANY, 1, 0,
409
           "Install systemd-boot to the ESP and EFI variables");
410

411
VERB_SCOPE(, verb_install, "update", NULL, VERB_ANY, 1, 0,
412
           "Update systemd-boot in the ESP and EFI variables");
413

414
VERB_SCOPE_NOARG(, verb_remove, "remove",
415
           "Remove systemd-boot from the ESP and EFI variables");
416

417
VERB_SCOPE_NOARG(, verb_is_installed, "is-installed",
418
           "Test whether systemd-boot is installed in the ESP");
419

420
VERB_SCOPE_NOARG(, verb_random_seed, "random-seed",
421
           "Initialize or refresh random seed in ESP and EFI variables");
422

423
VERB_GROUP("Kernel Image Commands");
424

425
VERB_SCOPE(, verb_kernel_identify, "kernel-identify", "KERNEL-IMAGE", 2, 2, 0,
426
           "Identify kernel image type");
427

428
VERB_SCOPE(, verb_kernel_inspect, "kernel-inspect", "KERNEL-IMAGE", 2, 2, 0,
429
           "Prints details about the kernel image");
430

431
static int parse_argv(int argc, char *argv[], char ***ret_args) {
345✔
432
        int r;
345✔
433

434
        assert(argc >= 0);
345✔
435
        assert(argv);
345✔
436

437
        OptionParser opts = { argc, argv };
345✔
438

439
        FOREACH_OPTION_OR_RETURN(c, &opts)
1,535✔
440
                switch (c) {
506✔
441

442
                OPTION_GROUP("Block Device Discovery Commands"): {}
61✔
443

444
                OPTION('p', "print-esp-path", NULL, "Print path to the EFI System Partition mount point"): {}
61✔
445
                OPTION_LONG("print-path", NULL, /* help= */ NULL):  /* Compatibility alias */
61✔
446
                        arg_print_esp_path = true;
61✔
447
                        break;
61✔
448

449
                OPTION('x', "print-boot-path", NULL, "Print path to the $BOOT partition mount point"):
9✔
450
                        arg_print_dollar_boot_path = true;
9✔
451
                        break;
9✔
452

453
                OPTION_LONG("print-loader-path", NULL, "Print path to currently booted boot loader binary"):
1✔
454
                        arg_print_loader_path = true;
1✔
455
                        break;
1✔
456

457
                OPTION_LONG("print-stub-path", NULL, "Print path to currently booted unified kernel binary"):
7✔
458
                        arg_print_stub_path = true;
7✔
459
                        break;
7✔
460

461
                OPTION('R', "print-root-device", NULL,
9✔
462
                       "Print path to the block device node backing the root file system"
463
                       " (returns e.g. /dev/nvme0n1p5)"): {}
9✔
464
                OPTION_HELP_VERBATIM("-RR",
9✔
465
                                     "Print path to the whole disk block device node backing the root FS"
466
                                     " (returns e.g. /dev/nvme0n1)"):
467
                        arg_print_root_device++;
9✔
468
                        break;
9✔
469

470
                OPTION_LONG("print-efi-architecture", NULL, "Print the local EFI architecture string"):
1✔
471
                        arg_print_efi_architecture = true;
1✔
472
                        break;
1✔
473

474
                OPTION_GROUP("Options"): {}
3✔
475

476
                OPTION_COMMON_HELP:
3✔
477
                        return help();
3✔
478

479
                OPTION_COMMON_VERSION:
3✔
480
                        return version();
3✔
481

482
                OPTION_LONG("esp-path", "PATH", "Path to the EFI System Partition (ESP)"): {}
3✔
483
                OPTION_LONG("path", "PATH", /* help= */ NULL):  /* Compatibility alias */
3✔
484
                        r = free_and_strdup(&arg_esp_path, opts.arg);
3✔
485
                        if (r < 0)
3✔
486
                                return log_oom();
×
487
                        break;
488

489
                OPTION_LONG("boot-path", "PATH", "Path to the $BOOT partition"):
3✔
490
                        r = free_and_strdup(&arg_xbootldr_path, opts.arg);
3✔
491
                        if (r < 0)
3✔
492
                                return log_oom();
×
493
                        break;
494

495
                OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
34✔
496
                        r = parse_path_argument(opts.arg, /* suppress_root= */ true, &arg_root);
34✔
497
                        if (r < 0)
34✔
498
                                return r;
499
                        break;
500

501
                OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
29✔
502
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &arg_image);
29✔
503
                        if (r < 0)
29✔
504
                                return r;
505
                        break;
506

507
                OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
×
508
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
509
                        if (r < 0)
×
510
                                return r;
511
                        break;
512

513
                OPTION_LONG("install-source", "SOURCE",
×
514
                            "Where to pick files when using --root=/--image= (auto, image, host)"): {
515
                        InstallSource is = install_source_from_string(opts.arg);
×
516
                        if (is < 0)
×
517
                                return log_error_errno(is, "Unexpected parameter for --install-source=: %s", opts.arg);
×
518

519
                        arg_install_source = is;
×
520
                        break;
×
521
                }
522

523
                OPTION_LONG("variables", "BOOL", "Whether to modify EFI variables"):
121✔
524
                        r = parse_tristate_argument_with_auto("--variables=", opts.arg, &arg_touch_variables);
121✔
525
                        if (r < 0)
121✔
526
                                return r;
527
#if !ENABLE_EFI
528
                        if (arg_touch_variables > 0)
529
                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
530
                                                       "Compiled without support for EFI, --variables=%s cannot be specified.", opts.arg);
531
#endif
532
                        break;
533

534
                OPTION_LONG("no-variables", NULL, /* help= */ NULL):  /* Compatibility alias */
×
535
                        arg_touch_variables = false;
×
536
                        break;
×
537

538
                OPTION_LONG("random-seed", "BOOL", "Whether to create random-seed file during install"):
×
539
                        r = parse_boolean_argument("--random-seed=", opts.arg, &arg_install_random_seed);
×
540
                        if (r < 0)
×
541
                                return r;
542
                        break;
543

544
                OPTION_COMMON_NO_PAGER:
×
545
                        arg_pager_flags |= PAGER_DISABLE;
×
546
                        break;
×
547

548
                OPTION_LONG("graceful", NULL,
141✔
549
                            "Don't fail when the ESP cannot be found or EFI variables cannot be written"):
550
                        _arg_graceful = ARG_GRACEFUL_YES;
141✔
551
                        break;
141✔
552

553
                OPTION('q', "quiet", NULL, "Suppress output"):
6✔
554
                        arg_quiet = true;
6✔
555
                        break;
6✔
556

557
                OPTION_COMMON_ENTRY_TOKEN:
20✔
558
                        r = parse_boot_entry_token_type(opts.arg, &arg_entry_token_type, &arg_entry_token);
20✔
559
                        if (r < 0)
20✔
560
                                return r;
561
                        break;
562

563
                OPTION_COMMON_MAKE_ENTRY_DIRECTORY: {}
14✔
564
                OPTION_LONG("make-machine-id-directory", "BOOL", /* help= */ NULL):  /* Compatibility alias */
14✔
565
                        if (streq(opts.arg, "auto"))  /* retained for backwards compatibility */
14✔
566
                                arg_make_entry_directory = -1; /* yes if machine-id is permanent */
×
567
                        else {
568
                                r = parse_boolean_argument("--make-entry-directory=", opts.arg, NULL);
14✔
569
                                if (r < 0)
14✔
570
                                        return r;
571

572
                                arg_make_entry_directory = r;
14✔
573
                        }
574
                        break;
575

576
                OPTION_COMMON_JSON:
7✔
577
                        r = parse_json_argument(opts.arg, &arg_json_format_flags);
7✔
578
                        if (r <= 0)
7✔
579
                                return r;
580
                        break;
581

582
                OPTION_LONG("all-architectures", NULL, "Install all supported EFI architectures"):
12✔
583
                        arg_arch_all = true;
12✔
584
                        break;
12✔
585

586
                OPTION_LONG("efi-boot-option-description", "DESCRIPTION",
×
587
                            "Description of the entry in the boot option list"):
588
                        if (!string_is_safe(opts.arg, STRING_ALLOW_BACKSLASHES|STRING_ALLOW_QUOTES|STRING_ALLOW_GLOBS)) {
×
589
                                _cleanup_free_ char *escaped = cescape(opts.arg);
×
590
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
591
                                                       "Invalid --efi-boot-option-description=: %s", strna(escaped));
592
                        }
593
                        if (strlen(opts.arg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
×
594
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
595
                                                       "--efi-boot-option-description= too long: %zu > %zu",
596
                                                       strlen(opts.arg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
597
                        r = free_and_strdup_warn(&arg_efi_boot_option_description, opts.arg);
×
598
                        if (r < 0)
×
599
                                return r;
600
                        break;
601

602
                OPTION_LONG("efi-boot-option-description-with-device", "BOOL",
×
603
                            "Suffix description with disk vendor/model/serial"):
604
                        r = parse_boolean_argument("--efi-boot-option-description-with-device=", opts.arg,
×
605
                                                   &arg_efi_boot_option_description_with_device);
606
                        if (r < 0)
×
607
                                return r;
608
                        break;
609

610
                OPTION_LONG("dry-run", NULL, "Dry run (unlink and cleanup)"):
1✔
611
                        arg_dry_run = true;
1✔
612
                        break;
1✔
613

614
                OPTION_LONG("secure-boot-auto-enroll", "BOOL", "Set up secure boot auto-enrollment"):
1✔
615
                        r = parse_boolean_argument("--secure-boot-auto-enroll=", opts.arg,
1✔
616
                                                   &arg_secure_boot_auto_enroll);
617
                        if (r < 0)
1✔
618
                                return r;
619
                        break;
620

621
                OPTION_COMMON_PRIVATE_KEY("Private key for Secure Boot auto-enrollment"):
1✔
622
                        r = free_and_strdup_warn(&arg_private_key, opts.arg);
1✔
623
                        if (r < 0)
1✔
624
                                return r;
625
                        break;
626

627
                OPTION_COMMON_PRIVATE_KEY_SOURCE:
×
628
                        r = parse_openssl_key_source_argument(opts.arg,
×
629
                                                              &arg_private_key_source,
630
                                                              &arg_private_key_source_type);
631
                        if (r < 0)
×
632
                                return r;
633
                        break;
634

635
                OPTION_COMMON_CERTIFICATE("PEM certificate to use when setting up Secure Boot auto-enrollment"):
1✔
636
                        r = free_and_strdup_warn(&arg_certificate, opts.arg);
1✔
637
                        if (r < 0)
1✔
638
                                return r;
639
                        break;
640

641
                OPTION_COMMON_CERTIFICATE_SOURCE:
×
642
                        r = parse_openssl_certificate_source_argument(opts.arg,
×
643
                                                                      &arg_certificate_source,
644
                                                                      &arg_certificate_source_type);
645
                        if (r < 0)
×
646
                                return r;
647
                        break;
648

649
                OPTION_LONG("oldest", "BOOL",
3✔
650
                            "Delete oldest boot menu entry"):
651
                        r = parse_boolean_argument("--oldest=", opts.arg, &arg_oldest);
3✔
652
                        if (r < 0)
3✔
653
                                return r;
654

655
                        break;
656

657
                OPTION_LONG("keep-free", "BYTES",
1✔
658
                            "How much space to keep free on ESP/XBOOTLDR"):
659

660
                        if (isempty(opts.arg))
1✔
661
                                arg_keep_free = KEEP_FREE_BYTES_DEFAULT;
×
662
                        else {
663
                                r = parse_size(opts.arg, 1024, &arg_keep_free);
1✔
664
                                if (r < 0)
1✔
665
                                        return log_error_errno(r, "Failed to parse --keep-free=: %s", opts.arg);
×
666
                        }
667

668
                        break;
669

670
                OPTION_LONG("entry-title", "TITLE",
1✔
671
                            "Selects the entry title for the new boot menu entry"):
672

673
                        if (isempty(opts.arg)) {
1✔
674
                                arg_entry_title = mfree(arg_entry_title);
×
675
                                break;
×
676
                        }
677

678
                        if (!efi_loader_entry_title_valid(opts.arg))
1✔
679
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid boot menu entry title: %s", opts.arg);
×
680

681
                        r = free_and_strdup_warn(&arg_entry_title, opts.arg);
1✔
682
                        if (r < 0)
1✔
683
                                return r;
684
                        break;
685

686
                OPTION_LONG("entry-version", "VERSION",
1✔
687
                            "Selects the entry version for the new boot menu entry"):
688
                        if (isempty(opts.arg)) {
1✔
689
                                arg_entry_version = mfree(arg_entry_version);
×
690
                                break;
×
691
                        }
692

693
                        if (!version_is_valid_versionspec(opts.arg))
1✔
694
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid boot menu entry version: %s", opts.arg);
×
695

696
                        r = free_and_strdup_warn(&arg_entry_version, opts.arg);
1✔
697
                        if (r < 0)
1✔
698
                                return r;
699
                        break;
700

701
                OPTION_LONG("entry-commit", "NR",
8✔
702
                            "Selects the entry commit version for the new boot menu entry"): {
703
                        if (isempty(opts.arg)) {
8✔
704
                                arg_entry_commit = 0;
×
705
                                break;
8✔
706
                        }
707

708
                        uint64_t n;
8✔
709
                        r = safe_atou64(opts.arg, &n);
8✔
710
                        if (r < 0)
8✔
711
                                return log_error_errno(r, "Failed to parse --entry-commit= parameter: %s", opts.arg);
×
712
                        if (!entry_commit_valid(n))
8✔
713
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid entry commit number.");
×
714

715
                        arg_entry_commit = n;
8✔
716
                        break;
8✔
717
                }
718

719
                OPTION('X', "extra", "PATH",
3✔
720
                       "Pass extra resource (confext, sysext, credential) to the invoked UKI of the boot menu entry"): {
721

722
                        if (isempty(opts.arg)) {
3✔
723
                                arg_extras = strv_free(arg_extras);
×
724
                                break;
3✔
725
                        }
726

727
                        _cleanup_free_ char *x = NULL;
×
728
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &x);
3✔
729
                        if (r < 0)
3✔
730
                                return r;
731

732
                        _cleanup_free_ char *fn = NULL;
3✔
733
                        r = path_extract_filename(x, &fn);
3✔
734
                        if (r < 0)
3✔
735
                                return log_error_errno(r, "Failed to extract filename from '%s': %m", x);
×
736
                        if (!efi_loader_entry_resource_filename_valid(fn))
3✔
737
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Extra filename '%s' is not suitable for reference in a boot menu entry.", fn);
×
738

739
                        r = strv_consume(&arg_extras, TAKE_PTR(x));
3✔
740
                        if (r < 0)
3✔
741
                                return log_oom();
×
742

743
                        strv_uniq(arg_extras);
3✔
744
                        break;
3✔
745
                }
746

747
                OPTION_LONG("tries-left", "NR",
1✔
748
                            "Set boot menu entries tries-left counter to the specified value"): {
749
                        if (isempty(opts.arg)) {
1✔
750
                                arg_tries_left = UINT_MAX;
×
751
                                break;
1✔
752
                        }
753

754
                        unsigned u;
1✔
755
                        r = safe_atou(opts.arg, &u);
1✔
756
                        if (r < 0)
1✔
757
                                return log_error_errno(r, "Failed to parse tries left counter: %s", opts.arg);
×
758
                        if (u >= UINT_MAX)
1✔
759
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Tries left counter too large, refusing: %u", u);
×
760

761
                        arg_tries_left = u;
1✔
762
                        break;
1✔
763
                }}
764

765
        char **args = option_parser_get_args(&opts);
339✔
766

767
        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)
339✔
768
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
769
                                                       "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path, --print-efi-architecture cannot be combined.");
770

771
        if ((arg_root || arg_image) && args[0] && !STR_IN_SET(args[0], "status", "list",
339✔
772
                        "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
773
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
774
                                       "Options --root= and --image= are not supported with verb %s.",
775
                                       args[0]);
776

777
        if (arg_root && arg_image)
339✔
778
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
×
779

780
        if (arg_install_source != INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
339✔
781
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
×
782

783
        if (arg_dry_run && args[0] && !STR_IN_SET(args[0], "unlink", "cleanup"))
339✔
784
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry-run is only supported with --unlink or --cleanup");
×
785

786
        if (arg_secure_boot_auto_enroll) {
339✔
787
#if HAVE_OPENSSL
788
                if (!arg_certificate)
1✔
789
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided.");
×
790

791
                if (!arg_private_key)
1✔
792
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided.");
×
793
#else
794
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Secure boot auto-enrollment requested but OpenSSL support is disabled.");
795
#endif
796
        }
797

798
        r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
339✔
799
        if (r < 0)
339✔
800
                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
×
801
        if (r > 0) {
339✔
802
                arg_varlink = true;
15✔
803
                arg_pager_flags |= PAGER_DISABLE;
15✔
804
        }
805

806
        *ret_args = args;
339✔
807
        return 1;
339✔
808
}
809

810
static int vl_server(void) {
15✔
811
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
15✔
812
        int r;
15✔
813

814
        /* Invocation as Varlink service */
815

816
        r = varlink_server_new(
15✔
817
                        &varlink_server,
818
                        SD_VARLINK_SERVER_ROOT_ONLY |
819
                        SD_VARLINK_SERVER_MYSELF_ONLY |
820
                        SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
821
                        /* userdata= */ NULL);
822
        if (r < 0)
15✔
823
                return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
824

825
        r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl);
15✔
826
        if (r < 0)
15✔
827
                return log_error_errno(r, "Failed to add Varlink interface: %m");
×
828

829
        r = sd_varlink_server_bind_method_many(
15✔
830
                        varlink_server,
831
                        "io.systemd.BootControl.ListBootEntries",     vl_method_list_boot_entries,
832
                        "io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
833
                        "io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware,
834
                        "io.systemd.BootControl.Install",             vl_method_install,
835
                        "io.systemd.BootControl.Link",                vl_method_link,
836
                        "io.systemd.BootControl.Unlink",              vl_method_unlink);
837
        if (r < 0)
15✔
838
                return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
839

840
        r = sd_varlink_server_loop_auto(varlink_server);
15✔
841
        if (r < 0)
15✔
842
                return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
843

844
        return 0;
845
}
846

847
static int run(int argc, char *argv[]) {
345✔
848
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
345✔
849
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
345✔
850
        int r;
345✔
851

852
        log_setup();
345✔
853

854
        char **args = NULL;
345✔
855
        r = parse_argv(argc, argv, &args);
345✔
856
        if (r <= 0)
345✔
857
                return r;
858

859
        if (arg_varlink)
339✔
860
                return vl_server();
15✔
861

862
        if (arg_print_root_device > 0) {
324✔
863
                _cleanup_free_ char *path = NULL;
6✔
864
                dev_t devno;
6✔
865

866
                r = blockdev_get_root(LOG_ERR, &devno);
6✔
867
                if (r < 0)
6✔
868
                        return r;
869
                if (r == 0) {
6✔
870
                        log_error("Root file system not backed by a (single) whole block device.");
×
871
                        return 80; /* some recognizable error code */
872
                }
873

874
                if (arg_print_root_device > 1) {
6✔
875
                        r = block_get_whole_disk(devno, &devno);
3✔
876
                        if (r < 0)
3✔
877
                                log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m");
×
878
                }
879

880
                r = device_path_make_canonical(S_IFBLK, devno, &path);
6✔
881
                if (r < 0)
6✔
882
                        return log_error_errno(r,
×
883
                                               "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
884
                                               DEVNUM_FORMAT_VAL(devno));
885

886
                puts(path);
6✔
887
                return 0;
888
        }
889

890
        if (arg_print_loader_path || arg_print_stub_path)
318✔
891
                return print_loader_or_stub_path();
8✔
892

893
        if (arg_print_efi_architecture) {
310✔
894
                printf("%s\n", get_efi_arch());
1✔
895
                return 0;
896
        }
897

898
        /* Open up and mount the image */
899
        if (arg_image) {
309✔
900
                assert(!arg_root);
26✔
901

902
                r = mount_image_privately_interactively(
26✔
903
                                arg_image,
904
                                arg_image_policy,
905
                                DISSECT_IMAGE_GENERIC_ROOT |
906
                                DISSECT_IMAGE_USR_NO_ROOT |
907
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
908
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
909
                                &mounted_dir,
910
                                /* ret_dir_fd= */ NULL,
911
                                &loop_device);
912
                if (r < 0)
26✔
913
                        return r;
914

915
                arg_root = strdup(mounted_dir);
26✔
916
                if (!arg_root)
26✔
917
                        return log_oom();
×
918
        }
919

920
        return dispatch_verb_with_args(args, NULL);
309✔
921
}
922

923
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
345✔
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