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

systemd / systemd / 13846995901

13 Mar 2025 08:25PM UTC coverage: 71.9% (+0.04%) from 71.864%
13846995901

push

github

yuwata
mkosi: update debian commit reference

* d8c7f8f7f4 Update changelog for 257.4-2 release
* f74cf88300 Split bootctl to new systemd-boot-tools package
* 10a8764966 Update changelog for 257.4-1 release

295942 of 411600 relevant lines covered (71.9%)

714834.66 hits per line

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

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

3
#include <getopt.h>
4

5
#include "sd-varlink.h"
6

7
#include "blockdev-util.h"
8
#include "bootctl.h"
9
#include "bootctl-install.h"
10
#include "bootctl-random-seed.h"
11
#include "bootctl-reboot-to-firmware.h"
12
#include "bootctl-set-efivar.h"
13
#include "bootctl-status.h"
14
#include "bootctl-uki.h"
15
#include "build.h"
16
#include "devnum-util.h"
17
#include "dissect-image.h"
18
#include "efi-loader.h"
19
#include "escape.h"
20
#include "find-esp.h"
21
#include "main-func.h"
22
#include "mount-util.h"
23
#include "pager.h"
24
#include "parse-argument.h"
25
#include "path-util.h"
26
#include "pretty-print.h"
27
#include "utf8.h"
28
#include "varlink-io.systemd.BootControl.h"
29
#include "varlink-util.h"
30
#include "verbs.h"
31
#include "virt.h"
32

33
/* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description
34
 * stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this
35
 * string, but we limit the length to something reasonable to prevent from the firmware
36
 * having to deal with a potentially too long string. */
37
#define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255)
38

39
char *arg_esp_path = NULL;
40
char *arg_xbootldr_path = NULL;
41
bool arg_print_esp_path = false;
42
bool arg_print_dollar_boot_path = false;
43
bool arg_print_loader_path = false;
44
bool arg_print_stub_path = false;
45
unsigned arg_print_root_device = 0;
46
bool arg_touch_variables = true;
47
bool arg_install_random_seed = true;
48
PagerFlags arg_pager_flags = 0;
49
bool arg_graceful = false;
50
bool arg_quiet = false;
51
int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */
52
sd_id128_t arg_machine_id = SD_ID128_NULL;
53
char *arg_install_layout = NULL;
54
BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
55
char *arg_entry_token = NULL;
56
sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
57
bool arg_arch_all = false;
58
char *arg_root = NULL;
59
char *arg_image = NULL;
60
InstallSource arg_install_source = ARG_INSTALL_SOURCE_AUTO;
61
char *arg_efi_boot_option_description = NULL;
62
bool arg_dry_run = false;
63
ImagePolicy *arg_image_policy = NULL;
64
bool arg_varlink = false;
65
bool arg_secure_boot_auto_enroll = false;
66
char *arg_certificate = NULL;
67
CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
68
char *arg_certificate_source = NULL;
69
char *arg_private_key = NULL;
70
KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
71
char *arg_private_key_source = NULL;
72

73
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
253✔
74
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
253✔
75
STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
253✔
76
STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
253✔
77
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
253✔
78
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
253✔
79
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
253✔
80
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
253✔
81
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
253✔
82
STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
253✔
83
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
253✔
84
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
253✔
85

86
int acquire_esp(
211✔
87
                int unprivileged_mode,
88
                bool graceful,
89
                uint32_t *ret_part,
90
                uint64_t *ret_pstart,
91
                uint64_t *ret_psize,
92
                sd_id128_t *ret_uuid,
93
                dev_t *ret_devid) {
94

95
        char *np;
211✔
96
        int r;
211✔
97

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

104
        r = find_esp_and_warn(arg_root, arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
211✔
105
        if (r == -ENOKEY) {
211✔
106
                if (graceful)
5✔
107
                        return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r,
×
108
                                              "Couldn't find EFI system partition, skipping.");
109

110
                return log_error_errno(r,
5✔
111
                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot/ or /efi/.\n"
112
                                       "Alternatively, use --esp-path= to specify path to mount point.");
113
        }
114
        if (r < 0)
206✔
115
                return r;
116

117
        free_and_replace(arg_esp_path, np);
205✔
118
        log_debug("Using EFI System Partition at %s.", arg_esp_path);
205✔
119

120
        return 0;
121
}
122

123
int acquire_xbootldr(
183✔
124
                int unprivileged_mode,
125
                sd_id128_t *ret_uuid,
126
                dev_t *ret_devid) {
127

128
        char *np;
183✔
129
        int r;
183✔
130

131
        r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid);
183✔
132
        if (r == -ENOKEY) {
183✔
133
                log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
90✔
134
                arg_xbootldr_path = mfree(arg_xbootldr_path);
90✔
135

136
                if (ret_uuid)
90✔
137
                        *ret_uuid = SD_ID128_NULL;
12✔
138
                if (ret_devid)
90✔
139
                        *ret_devid = 0;
17✔
140
                return 0;
90✔
141
        }
142
        if (r < 0)
93✔
143
                return r;
144

145
        free_and_replace(arg_xbootldr_path, np);
92✔
146
        log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
92✔
147

148
        return 1;
149
}
150

151
static int print_loader_or_stub_path(void) {
6✔
152
        _cleanup_free_ char *p = NULL;
6✔
153
        sd_id128_t uuid;
6✔
154
        int r;
6✔
155

156
        if (arg_print_loader_path) {
6✔
157
                r = efi_loader_get_device_part_uuid(&uuid);
×
158
                if (r == -ENOENT)
×
159
                        return log_error_errno(r, "No loader partition UUID passed.");
×
160
                if (r < 0)
×
161
                        return log_error_errno(r, "Unable to determine loader partition UUID: %m");
×
162

163
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &p);
×
164
                if (r == -ENOENT)
×
165
                        return log_error_errno(r, "No loader EFI binary path passed.");
×
166
                if (r < 0)
×
167
                        return log_error_errno(r, "Unable to determine loader EFI binary path: %m");
×
168
        } else {
169
                assert(arg_print_stub_path);
6✔
170

171
                r = efi_stub_get_device_part_uuid(&uuid);
6✔
172
                if (r == -ENOENT)
6✔
173
                        return log_error_errno(r, "No stub partition UUID passed.");
×
174
                if (r < 0)
6✔
175
                        return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
176

177
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &p);
6✔
178
                if (r == -ENOENT)
6✔
179
                        return log_error_errno(r, "No stub EFI binary path passed.");
×
180
                if (r < 0)
6✔
181
                        return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
182
        }
183

184
        sd_id128_t esp_uuid;
6✔
185
        r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false,
6✔
186
                        /* ret_part= */ NULL, /* ret_pstart= */ NULL, /* ret_psize= */ NULL,
187
                        &esp_uuid, /* ret_devid= */ NULL);
188
        if (r < 0)
6✔
189
                return r;
190

191
        const char *found_path = NULL;
6✔
192
        if (sd_id128_equal(esp_uuid, uuid))
6✔
193
                found_path = arg_esp_path;
6✔
194
        else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
×
195
                sd_id128_t xbootldr_uuid;
×
196

197
                r = acquire_xbootldr(/* unprivileged_mode= */ false, &xbootldr_uuid, /* ret_devid= */ NULL);
×
198
                if (r < 0)
×
199
                        return r;
×
200

201
                if (sd_id128_equal(xbootldr_uuid, uuid))
×
202
                        found_path = arg_xbootldr_path;
×
203
        }
204

205
        if (!found_path)
6✔
206
                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to discover partition " SD_ID128_FORMAT_STR " among mounted boot partitions.", SD_ID128_FORMAT_VAL(uuid));
×
207

208
        _cleanup_free_ char *j = path_join(found_path, p);
12✔
209
        if (!j)
6✔
210
                return log_oom();
×
211

212
        puts(j);
6✔
213
        return 0;
214
}
215

216
static int help(int argc, char *argv[], void *userdata) {
3✔
217
        _cleanup_free_ char *link = NULL;
3✔
218
        int r;
3✔
219

220
        pager_open(arg_pager_flags);
3✔
221

222
        r = terminal_urlify_man("bootctl", "1", &link);
3✔
223
        if (r < 0)
3✔
224
                return log_oom();
×
225

226
        printf("%1$s [OPTIONS...] COMMAND ...\n"
6✔
227
               "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
228
               "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
229
               "  status               Show status of installed boot loader and EFI variables\n"
230
               "  reboot-to-firmware [BOOL]\n"
231
               "                       Query or set reboot-to-firmware EFI flag\n"
232
               "\n%3$sBoot Loader Specification Commands:%4$s\n"
233
               "  list                 List boot loader entries\n"
234
               "  unlink ID            Remove boot loader entry\n"
235
               "  cleanup              Remove files in ESP not referenced in any boot entry\n"
236
               "\n%3$sBoot Loader Interface Commands:%4$s\n"
237
               "  set-default ID       Set default boot loader entry\n"
238
               "  set-oneshot ID       Set default boot loader entry, for next boot only\n"
239
               "  set-timeout SECONDS  Set the menu timeout\n"
240
               "  set-timeout-oneshot SECONDS\n"
241
               "                       Set the menu timeout for the next boot only\n"
242
               "\n%3$ssystemd-boot Commands:%4$s\n"
243
               "  install              Install systemd-boot to the ESP and EFI variables\n"
244
               "  update               Update systemd-boot in the ESP and EFI variables\n"
245
               "  remove               Remove systemd-boot from the ESP and EFI variables\n"
246
               "  is-installed         Test whether systemd-boot is installed in the ESP\n"
247
               "  random-seed          Initialize or refresh random seed in ESP and EFI\n"
248
               "                       variables\n"
249
               "\n%3$sKernel Image Commands:%4$s\n"
250
               "  kernel-identify      Identify kernel image type\n"
251
               "  kernel-inspect       Prints details about the kernel image\n"
252
               "\n%3$sBlock Device Discovery Commands:%4$s\n"
253
               "  -p --print-esp-path  Print path to the EFI System Partition mount point\n"
254
               "  -x --print-boot-path Print path to the $BOOT partition mount point\n"
255
               "     --print-loader-path\n"
256
               "                       Print path to currently booted boot loader binary\n"
257
               "     --print-stub-path Print path to currently booted unified kernel binary\n"
258
               "  -R --print-root-device\n"
259
               "                       Print path to the block device node backing the\n"
260
               "                       root file system (returns e.g. /dev/nvme0n1p5)\n"
261
               "  -RR                  Print path to the whole disk block device node\n"
262
               "                       backing the root FS (returns e.g. /dev/nvme0n1)\n"
263
               "\n%3$sOptions:%4$s\n"
264
               "  -h --help            Show this help\n"
265
               "     --version         Print version\n"
266
               "     --esp-path=PATH   Path to the EFI System Partition (ESP)\n"
267
               "     --boot-path=PATH  Path to the $BOOT partition\n"
268
               "     --root=PATH       Operate on an alternate filesystem root\n"
269
               "     --image=PATH      Operate on disk image as filesystem root\n"
270
               "     --image-policy=POLICY\n"
271
               "                       Specify disk image dissection policy\n"
272
               "     --install-source=auto|image|host\n"
273
               "                       Where to pick files when using --root=/--image=\n"
274
               "     --no-variables    Don't touch EFI variables\n"
275
               "     --random-seed=yes|no\n"
276
               "                       Whether to create random-seed file during install\n"
277
               "     --no-pager        Do not pipe output into a pager\n"
278
               "     --graceful        Don't fail when the ESP cannot be found or EFI\n"
279
               "                       variables cannot be written\n"
280
               "  -q --quiet           Suppress output\n"
281
               "     --make-entry-directory=yes|no|auto\n"
282
               "                       Create $BOOT/ENTRY-TOKEN/ directory\n"
283
               "     --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
284
               "                       Entry token to use for this installation\n"
285
               "     --json=pretty|short|off\n"
286
               "                       Generate JSON output\n"
287
               "     --all-architectures\n"
288
               "                       Install all supported EFI architectures\n"
289
               "     --efi-boot-option-description=DESCRIPTION\n"
290
               "                       Description of the entry in the boot option list\n"
291
               "     --dry-run         Dry run (unlink and cleanup)\n"
292
               "     --secure-boot-auto-enroll\n"
293
               "                       Set up secure boot auto-enrollment\n"
294
               "     --private-key=PATH|URI\n"
295
               "                       Private key to use when setting up secure boot\n"
296
               "                       auto-enrollment or an engine or provider specific\n"
297
               "                       designation if --private-key-source= is used\n"
298
               "     --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
299
               "                       Specify how to use KEY for --private-key=. Allows\n"
300
               "                       an OpenSSL engine/provider to be used when setting\n"
301
               "                       up secure boot auto-enrollment\n"
302
               "     --certificate=PATH|URI\n"
303
               "                       PEM certificate to use when setting up Secure Boot\n"
304
               "                       auto-enrollment, or a provider specific designation\n"
305
               "                       if --certificate-source= is used\n"
306
               "     --certificate-source=file|provider:PROVIDER\n"
307
               "                       Specify how to interpret the certificate from\n"
308
               "                       --certificate=. Allows the certificate to be loaded\n"
309
               "                       from an OpenSSL provider\n"
310
               "\nSee the %2$s for details.\n",
311
               program_invocation_short_name,
312
               link,
313
               ansi_underline(),
314
               ansi_normal(),
315
               ansi_highlight(),
316
               ansi_normal());
317

318
        return 0;
319
}
320

321
static int parse_argv(int argc, char *argv[]) {
253✔
322
        enum {
253✔
323
                ARG_ESP_PATH = 0x100,
324
                ARG_BOOT_PATH,
325
                ARG_ROOT,
326
                ARG_IMAGE,
327
                ARG_IMAGE_POLICY,
328
                ARG_INSTALL_SOURCE,
329
                ARG_VERSION,
330
                ARG_NO_VARIABLES,
331
                ARG_RANDOM_SEED,
332
                ARG_NO_PAGER,
333
                ARG_GRACEFUL,
334
                ARG_MAKE_ENTRY_DIRECTORY,
335
                ARG_ENTRY_TOKEN,
336
                ARG_JSON,
337
                ARG_ARCH_ALL,
338
                ARG_EFI_BOOT_OPTION_DESCRIPTION,
339
                ARG_DRY_RUN,
340
                ARG_PRINT_LOADER_PATH,
341
                ARG_PRINT_STUB_PATH,
342
                ARG_SECURE_BOOT_AUTO_ENROLL,
343
                ARG_CERTIFICATE,
344
                ARG_CERTIFICATE_SOURCE,
345
                ARG_PRIVATE_KEY,
346
                ARG_PRIVATE_KEY_SOURCE,
347
        };
348

349
        static const struct option options[] = {
253✔
350
                { "help",                        no_argument,       NULL, 'h'                             },
351
                { "version",                     no_argument,       NULL, ARG_VERSION                     },
352
                { "esp-path",                    required_argument, NULL, ARG_ESP_PATH                    },
353
                { "path",                        required_argument, NULL, ARG_ESP_PATH                    }, /* Compatibility alias */
354
                { "boot-path",                   required_argument, NULL, ARG_BOOT_PATH                   },
355
                { "root",                        required_argument, NULL, ARG_ROOT                        },
356
                { "image",                       required_argument, NULL, ARG_IMAGE                       },
357
                { "image-policy",                required_argument, NULL, ARG_IMAGE_POLICY                },
358
                { "install-source",              required_argument, NULL, ARG_INSTALL_SOURCE              },
359
                { "print-esp-path",              no_argument,       NULL, 'p'                             },
360
                { "print-path",                  no_argument,       NULL, 'p'                             }, /* Compatibility alias */
361
                { "print-boot-path",             no_argument,       NULL, 'x'                             },
362
                { "print-loader-path",           no_argument,       NULL, ARG_PRINT_LOADER_PATH           },
363
                { "print-stub-path",             no_argument,       NULL, ARG_PRINT_STUB_PATH             },
364
                { "print-root-device",           no_argument,       NULL, 'R'                             },
365
                { "no-variables",                no_argument,       NULL, ARG_NO_VARIABLES                },
366
                { "random-seed",                 required_argument, NULL, ARG_RANDOM_SEED                 },
367
                { "no-pager",                    no_argument,       NULL, ARG_NO_PAGER                    },
368
                { "graceful",                    no_argument,       NULL, ARG_GRACEFUL                    },
369
                { "quiet",                       no_argument,       NULL, 'q'                             },
370
                { "make-entry-directory",        required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY        },
371
                { "make-machine-id-directory",   required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY        }, /* Compatibility alias */
372
                { "entry-token",                 required_argument, NULL, ARG_ENTRY_TOKEN                 },
373
                { "json",                        required_argument, NULL, ARG_JSON                        },
374
                { "all-architectures",           no_argument,       NULL, ARG_ARCH_ALL                    },
375
                { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
376
                { "dry-run",                     no_argument,       NULL, ARG_DRY_RUN                     },
377
                { "secure-boot-auto-enroll",     required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL     },
378
                { "certificate",                 required_argument, NULL, ARG_CERTIFICATE                 },
379
                { "certificate-source",          required_argument, NULL, ARG_CERTIFICATE_SOURCE          },
380
                { "private-key",                 required_argument, NULL, ARG_PRIVATE_KEY                 },
381
                { "private-key-source",          required_argument, NULL, ARG_PRIVATE_KEY_SOURCE          },
382
                {}
383
        };
384

385
        int c, r;
253✔
386

387
        assert(argc >= 0);
253✔
388
        assert(argv);
253✔
389

390
        while ((c = getopt_long(argc, argv, "hpxRq", options, NULL)) >= 0)
640✔
391
                switch (c) {
393✔
392

393
                case 'h':
3✔
394
                        help(0, NULL, NULL);
3✔
395
                        return 0;
3✔
396

397
                case ARG_VERSION:
3✔
398
                        return version();
3✔
399

400
                case ARG_ESP_PATH:
3✔
401
                        r = free_and_strdup(&arg_esp_path, optarg);
3✔
402
                        if (r < 0)
3✔
403
                                return log_oom();
×
404
                        break;
405

406
                case ARG_BOOT_PATH:
3✔
407
                        r = free_and_strdup(&arg_xbootldr_path, optarg);
3✔
408
                        if (r < 0)
3✔
409
                                return log_oom();
×
410
                        break;
411

412
                case ARG_ROOT:
34✔
413
                        r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
34✔
414
                        if (r < 0)
34✔
415
                                return r;
416
                        break;
417

418
                case ARG_IMAGE:
29✔
419
                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
29✔
420
                        if (r < 0)
29✔
421
                                return r;
422
                        break;
423

424
                case ARG_IMAGE_POLICY:
×
425
                        r = parse_image_policy_argument(optarg, &arg_image_policy);
×
426
                        if (r < 0)
×
427
                                return r;
428
                        break;
429

430
                case ARG_INSTALL_SOURCE:
×
431
                        if (streq(optarg, "auto"))
×
432
                                arg_install_source = ARG_INSTALL_SOURCE_AUTO;
×
433
                        else if (streq(optarg, "image"))
×
434
                                arg_install_source = ARG_INSTALL_SOURCE_IMAGE;
×
435
                        else if (streq(optarg, "host"))
×
436
                                arg_install_source = ARG_INSTALL_SOURCE_HOST;
×
437
                        else
438
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
439
                                                       "Unexpected parameter for --install-source=: %s", optarg);
440

441
                        break;
442

443
                case 'p':
10✔
444
                        arg_print_esp_path = true;
10✔
445
                        break;
10✔
446

447
                case 'x':
8✔
448
                        arg_print_dollar_boot_path = true;
8✔
449
                        break;
8✔
450

451
                case ARG_PRINT_LOADER_PATH:
×
452
                        arg_print_loader_path = true;
×
453
                        break;
×
454

455
                case ARG_PRINT_STUB_PATH:
6✔
456
                        arg_print_stub_path = true;
6✔
457
                        break;
6✔
458

459
                case 'R':
7✔
460
                        arg_print_root_device++;
7✔
461
                        break;
7✔
462

463
                case ARG_NO_VARIABLES:
115✔
464
                        arg_touch_variables = false;
115✔
465
                        break;
115✔
466

467
                case ARG_RANDOM_SEED:
×
468
                        r = parse_boolean_argument("--random-seed=", optarg, &arg_install_random_seed);
×
469
                        if (r < 0)
×
470
                                return r;
471
                        break;
472

473
                case ARG_NO_PAGER:
×
474
                        arg_pager_flags |= PAGER_DISABLE;
×
475
                        break;
×
476

477
                case ARG_GRACEFUL:
132✔
478
                        arg_graceful = true;
132✔
479
                        break;
132✔
480

481
                case 'q':
6✔
482
                        arg_quiet = true;
6✔
483
                        break;
6✔
484

485
                case ARG_ENTRY_TOKEN:
×
486
                        r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token);
×
487
                        if (r < 0)
×
488
                                return r;
489
                        break;
490

491
                case ARG_MAKE_ENTRY_DIRECTORY:
13✔
492
                        if (streq(optarg, "auto"))  /* retained for backwards compatibility */
13✔
493
                                arg_make_entry_directory = -1; /* yes if machine-id is permanent */
×
494
                        else {
495
                                r = parse_boolean_argument("--make-entry-directory=", optarg, NULL);
13✔
496
                                if (r < 0)
13✔
497
                                        return r;
498

499
                                arg_make_entry_directory = r;
13✔
500
                        }
501
                        break;
502

503
                case ARG_JSON:
6✔
504
                        r = parse_json_argument(optarg, &arg_json_format_flags);
6✔
505
                        if (r <= 0)
6✔
506
                                return r;
507
                        break;
508

509
                case ARG_ARCH_ALL:
12✔
510
                        arg_arch_all = true;
12✔
511
                        break;
12✔
512

513
                case ARG_EFI_BOOT_OPTION_DESCRIPTION:
×
514
                        if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) {
×
515
                                _cleanup_free_ char *escaped = NULL;
×
516

517
                                escaped = cescape(optarg);
×
518
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
519
                                                       "Invalid --efi-boot-option-description=: %s", strna(escaped));
520
                        }
521
                        if (strlen(optarg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
×
522
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
523
                                                       "--efi-boot-option-description= too long: %zu > %zu",
524
                                                       strlen(optarg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
525
                        r = free_and_strdup_warn(&arg_efi_boot_option_description, optarg);
×
526
                        if (r < 0)
×
527
                                return r;
528
                        break;
529

530
                case ARG_DRY_RUN:
×
531
                        arg_dry_run = true;
×
532
                        break;
×
533

534
                case ARG_SECURE_BOOT_AUTO_ENROLL:
1✔
535
                        r = parse_boolean_argument("--secure-boot-auto-enroll=", optarg, &arg_secure_boot_auto_enroll);
1✔
536
                        if (r < 0)
1✔
537
                                return r;
538
                        break;
539

540
                case ARG_CERTIFICATE:
1✔
541
                        r = free_and_strdup_warn(&arg_certificate, optarg);
1✔
542
                        if (r < 0)
1✔
543
                                return r;
544
                        break;
545

546
                case ARG_CERTIFICATE_SOURCE:
×
547
                        r = parse_openssl_certificate_source_argument(
×
548
                                        optarg,
549
                                        &arg_certificate_source,
550
                                        &arg_certificate_source_type);
551
                        if (r < 0)
×
552
                                return r;
553
                        break;
554

555
                case ARG_PRIVATE_KEY: {
1✔
556
                        r = free_and_strdup_warn(&arg_private_key, optarg);
1✔
557
                        if (r < 0)
1✔
558
                                return r;
559
                        break;
560
                }
561

562
                case ARG_PRIVATE_KEY_SOURCE:
×
563
                        r = parse_openssl_key_source_argument(
×
564
                                        optarg,
565
                                        &arg_private_key_source,
566
                                        &arg_private_key_source_type);
567
                        if (r < 0)
×
568
                                return r;
569
                        break;
570

571
                case '?':
572
                        return -EINVAL;
573

574
                default:
×
575
                        assert_not_reached();
×
576
                }
577

578
        if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path > 1)
247✔
579
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
580
                                                       "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path cannot be combined.");
581

582
        if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
247✔
583
                        "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
584
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
585
                                       "Options --root= and --image= are not supported with verb %s.",
586
                                       argv[optind]);
587

588
        if (arg_root && arg_image)
247✔
589
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
×
590

591
        if (arg_install_source != ARG_INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
247✔
592
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
×
593

594
        if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
247✔
595
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup");
×
596

597
        if (arg_secure_boot_auto_enroll && !arg_certificate)
247✔
598
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided");
×
599

600
        if (arg_secure_boot_auto_enroll && !arg_private_key)
247✔
601
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided");
×
602

603
        r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
247✔
604
        if (r < 0)
247✔
605
                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
×
606
        if (r > 0) {
247✔
607
                arg_varlink = true;
16✔
608
                arg_pager_flags |= PAGER_DISABLE;
16✔
609
        }
610

611
        return 1;
612
}
613

614
static int bootctl_main(int argc, char *argv[]) {
220✔
615
        static const Verb verbs[] = {
220✔
616
                { "help",                VERB_ANY, VERB_ANY, 0,            help                     },
617
                { "status",              VERB_ANY, 1,        VERB_DEFAULT, verb_status              },
618
                { "install",             VERB_ANY, 1,        0,            verb_install             },
619
                { "update",              VERB_ANY, 1,        0,            verb_install             },
620
                { "remove",              VERB_ANY, 1,        0,            verb_remove              },
621
                { "is-installed",        VERB_ANY, 1,        0,            verb_is_installed        },
622
                { "kernel-identify",     2,        2,        0,            verb_kernel_identify     },
623
                { "kernel-inspect",      2,        2,        0,            verb_kernel_inspect      },
624
                { "list",                VERB_ANY, 1,        0,            verb_list                },
625
                { "unlink",              2,        2,        0,            verb_unlink              },
626
                { "cleanup",             VERB_ANY, 1,        0,            verb_list                },
627
                { "set-default",         2,        2,        0,            verb_set_efivar          },
628
                { "set-oneshot",         2,        2,        0,            verb_set_efivar          },
629
                { "set-timeout",         2,        2,        0,            verb_set_efivar          },
630
                { "set-timeout-oneshot", 2,        2,        0,            verb_set_efivar          },
631
                { "random-seed",         VERB_ANY, 1,        0,            verb_random_seed         },
632
                { "reboot-to-firmware",  VERB_ANY, 2,        0,            verb_reboot_to_firmware  },
633
                {}
634
        };
635

636
        return dispatch_verb(argc, argv, verbs, NULL);
220✔
637
}
638

639
static int run(int argc, char *argv[]) {
253✔
640
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
253✔
641
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
253✔
642
        int r;
253✔
643

644
        log_setup();
253✔
645

646
        /* If we run in a container, automatically turn off EFI file system access */
647
        if (detect_container() > 0)
253✔
648
                arg_touch_variables = false;
66✔
649

650
        r = parse_argv(argc, argv);
253✔
651
        if (r <= 0)
253✔
652
                return r;
653

654
        if (arg_varlink) {
247✔
655
                _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
16✔
656

657
                /* Invocation as Varlink service */
658

659
                r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_ROOT_ONLY, NULL);
16✔
660
                if (r < 0)
16✔
661
                        return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
662

663
                r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl);
16✔
664
                if (r < 0)
16✔
665
                        return log_error_errno(r, "Failed to add Varlink interface: %m");
×
666

667
                r = sd_varlink_server_bind_method_many(
16✔
668
                                varlink_server,
669
                                "io.systemd.BootControl.ListBootEntries",     vl_method_list_boot_entries,
670
                                "io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
671
                                "io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware);
672
                if (r < 0)
16✔
673
                        return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
674

675
                r = sd_varlink_server_loop_auto(varlink_server);
16✔
676
                if (r < 0)
16✔
677
                        return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
678

679
                return EXIT_SUCCESS;
680
        }
681

682
        if (arg_print_root_device > 0) {
231✔
683
                _cleanup_free_ char *path = NULL;
5✔
684
                dev_t devno;
5✔
685

686
                r = blockdev_get_root(LOG_ERR, &devno);
5✔
687
                if (r < 0)
5✔
688
                        return r;
689
                if (r == 0) {
5✔
690
                        log_error("Root file system not backed by a (single) whole block device.");
×
691
                        return 80; /* some recognizable error code */
×
692
                }
693

694
                if (arg_print_root_device > 1) {
5✔
695
                        r = block_get_whole_disk(devno, &devno);
2✔
696
                        if (r < 0)
2✔
697
                                log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m");
×
698
                }
699

700
                r = device_path_make_canonical(S_IFBLK, devno, &path);
5✔
701
                if (r < 0)
5✔
702
                        return log_error_errno(r,
×
703
                                               "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
704
                                               DEVNUM_FORMAT_VAL(devno));
705

706
                puts(path);
5✔
707
                return 0;
708
        }
709

710
        if (arg_print_loader_path || arg_print_stub_path)
226✔
711
                return print_loader_or_stub_path();
6✔
712

713
        /* Open up and mount the image */
714
        if (arg_image) {
220✔
715
                assert(!arg_root);
26✔
716

717
                r = mount_image_privately_interactively(
26✔
718
                                arg_image,
719
                                arg_image_policy,
720
                                DISSECT_IMAGE_GENERIC_ROOT |
721
                                DISSECT_IMAGE_USR_NO_ROOT |
722
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
723
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
724
                                &mounted_dir,
725
                                /* ret_dir_fd= */ NULL,
726
                                &loop_device);
727
                if (r < 0)
26✔
728
                        return r;
729

730
                arg_root = strdup(mounted_dir);
26✔
731
                if (!arg_root)
26✔
732
                        return log_oom();
×
733
        }
734

735
        return bootctl_main(argc, argv);
220✔
736
}
737

738
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
506✔
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