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

systemd / systemd / 22121027152

17 Feb 2026 11:25PM UTC coverage: 72.452% (-0.2%) from 72.633%
22121027152

push

github

yuwata
pe-binary: fix missing le16toh() on NumberOfSections in pe_hash/uki_hash

pe_hash() and uki_hash() pass pe_header->pe.NumberOfSections directly
to typesafe_qsort() and FOREACH_ARRAY() without le16toh(). On
big-endian (s390x), NumberOfSections=3 gets read as 0x0300 (768),
while pe_load_sections() correctly converts it and only allocates 3
sections. This makes qsort process 768 elements on a 3-element
buffer, causing a heap-buffer-overflow (confirmed with ASAN on
native s390x).

Wrap all three raw usages with le16toh() to match pe_load_sections().

3 of 4 new or added lines in 1 file covered. (75.0%)

5909 existing lines in 86 files now uncovered.

312380 of 431157 relevant lines covered (72.45%)

1145272.29 hits per line

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

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

3
#include <getopt.h>
4
#include <sys/stat.h>
5

6
#include "sd-varlink.h"
7

8
#include "blockdev-util.h"
9
#include "boot-entry.h"
10
#include "bootctl.h"
11
#include "bootctl-cleanup.h"
12
#include "bootctl-install.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 "build.h"
20
#include "devnum-util.h"
21
#include "dissect-image.h"
22
#include "efi-loader.h"
23
#include "efivars.h"
24
#include "escape.h"
25
#include "find-esp.h"
26
#include "image-policy.h"
27
#include "log.h"
28
#include "loop-util.h"
29
#include "main-func.h"
30
#include "mount-util.h"
31
#include "openssl-util.h"
32
#include "pager.h"
33
#include "parse-argument.h"
34
#include "path-util.h"
35
#include "pretty-print.h"
36
#include "string-table.h"
37
#include "string-util.h"
38
#include "strv.h"
39
#include "utf8.h"
40
#include "varlink-io.systemd.BootControl.h"
41
#include "varlink-util.h"
42
#include "verbs.h"
43
#include "virt.h"
44

45
static GracefulMode _arg_graceful = ARG_GRACEFUL_NO;
46

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

81
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
298✔
82
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
298✔
83
STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
298✔
84
STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
298✔
85
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
298✔
86
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
298✔
87
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
298✔
88
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
298✔
89
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
298✔
90
STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
298✔
91
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
298✔
92
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
298✔
93

94
static const char* const install_source_table[_INSTALL_SOURCE_MAX] = {
95
        [INSTALL_SOURCE_IMAGE] = "image",
96
        [INSTALL_SOURCE_HOST]  = "host",
97
        [INSTALL_SOURCE_AUTO]  = "auto",
98
};
99

100
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_source, InstallSource);
×
101

102
int acquire_esp(
256✔
103
                int unprivileged_mode,
104
                bool graceful,
105
                uint32_t *ret_part,
106
                uint64_t *ret_pstart,
107
                uint64_t *ret_psize,
108
                sd_id128_t *ret_uuid,
109
                dev_t *ret_devid) {
110

111
        char *np;
256✔
112
        int r;
256✔
113

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

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

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

133
        free_and_replace(arg_esp_path, np);
250✔
134
        log_debug("Using EFI System Partition at %s.", arg_esp_path);
250✔
135

136
        return 0;
137
}
138

139
int acquire_xbootldr(
203✔
140
                int unprivileged_mode,
141
                sd_id128_t *ret_uuid,
142
                dev_t *ret_devid) {
143

144
        char *np;
203✔
145
        int r;
203✔
146

147
        r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid);
203✔
148
        if (r == -ENOKEY) {
203✔
149
                log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
101✔
150
                arg_xbootldr_path = mfree(arg_xbootldr_path);
101✔
151

152
                if (ret_uuid)
101✔
153
                        *ret_uuid = SD_ID128_NULL;
15✔
154
                if (ret_devid)
101✔
155
                        *ret_devid = 0;
20✔
156
                return 0;
101✔
157
        }
158
        if (r < 0)
102✔
159
                return r;
160

161
        free_and_replace(arg_xbootldr_path, np);
101✔
162
        log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
101✔
163

164
        return 1;
165
}
166

167
static int print_loader_or_stub_path(void) {
6✔
168
        _cleanup_free_ char *p = NULL;
6✔
169
        sd_id128_t uuid;
6✔
170
        int r;
6✔
171

172
        if (arg_print_loader_path) {
6✔
173
                r = efi_loader_get_device_part_uuid(&uuid);
×
174
                if (r == -ENOENT)
×
175
                        return log_error_errno(r, "No loader partition UUID passed.");
×
176
                if (r < 0)
×
177
                        return log_error_errno(r, "Unable to determine loader partition UUID: %m");
×
178

179
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &p);
×
180
                if (r == -ENOENT)
×
181
                        return log_error_errno(r, "No loader EFI binary path passed.");
×
182
                if (r < 0)
×
183
                        return log_error_errno(r, "Unable to determine loader EFI binary path: %m");
×
184
        } else {
185
                assert(arg_print_stub_path);
6✔
186

187
                r = efi_stub_get_device_part_uuid(&uuid);
6✔
188
                if (r == -ENOENT)
6✔
189
                        return log_error_errno(r, "No stub partition UUID passed.");
×
190
                if (r < 0)
6✔
191
                        return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
192

193
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &p);
6✔
194
                if (r == -ENOENT)
6✔
195
                        return log_error_errno(r, "No stub EFI binary path passed.");
×
196
                if (r < 0)
6✔
197
                        return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
198
        }
199

200
        sd_id128_t esp_uuid;
6✔
201
        r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false,
6✔
202
                        /* ret_part= */ NULL, /* ret_pstart= */ NULL, /* ret_psize= */ NULL,
203
                        &esp_uuid, /* ret_devid= */ NULL);
204
        if (r < 0)
6✔
205
                return r;
206

207
        const char *found_path = NULL;
6✔
208
        if (sd_id128_equal(esp_uuid, uuid))
6✔
209
                found_path = arg_esp_path;
6✔
210
        else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
×
211
                sd_id128_t xbootldr_uuid;
×
212

213
                r = acquire_xbootldr(/* unprivileged_mode= */ false, &xbootldr_uuid, /* ret_devid= */ NULL);
×
214
                if (r < 0)
×
215
                        return r;
×
216

217
                if (sd_id128_equal(xbootldr_uuid, uuid))
×
218
                        found_path = arg_xbootldr_path;
×
219
        }
220

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

224
        _cleanup_free_ char *j = path_join(found_path, p);
12✔
225
        if (!j)
6✔
226
                return log_oom();
×
227

228
        puts(j);
6✔
229
        return 0;
230
}
231

232
bool touch_variables(void) {
17✔
233
        /* If we run in a container or on a non-EFI system, automatically turn off EFI file system access,
234
         * unless explicitly overridden. */
235

236
        if (arg_touch_variables >= 0)
17✔
237
                return arg_touch_variables;
×
238

239
        if (arg_root) {
17✔
240
                log_once(LOG_NOTICE,
3✔
241
                         "Operating on %s, skipping EFI variable modifications.",
242
                         arg_image ? "image" : "root directory");
243
                return false;
2✔
244
        }
245

246
        if (!is_efi_boot()) { /* NB: this internally checks if we run in a container */
15✔
247
                log_once(LOG_NOTICE,
×
248
                         "Not booted with EFI or running in a container, skipping EFI variable modifications.");
249
                return false;
×
250
        }
251

252
        return true;
253
}
254

255
GracefulMode arg_graceful(void) {
288✔
256
        static bool chroot_checked = false;
288✔
257

258
        if (!chroot_checked && running_in_chroot() > 0) {
288✔
259
                if (_arg_graceful == ARG_GRACEFUL_NO)
×
260
                        log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Running in a chroot, enabling --graceful.");
×
261

262
                _arg_graceful = ARG_GRACEFUL_FORCE;
×
263
        }
264

265
        chroot_checked = true;
288✔
266

267
        return _arg_graceful;
288✔
268
}
269

270
static int help(int argc, char *argv[], void *userdata) {
3✔
271
        _cleanup_free_ char *link = NULL;
3✔
272
        int r;
3✔
273

274
        pager_open(arg_pager_flags);
3✔
275

276
        r = terminal_urlify_man("bootctl", "1", &link);
3✔
277
        if (r < 0)
3✔
278
                return log_oom();
×
279

280
        printf("%1$s [OPTIONS...] COMMAND ...\n"
6✔
281
               "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
282
               "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
283
               "  status               Show status of installed boot loader and EFI variables\n"
284
               "  reboot-to-firmware [BOOL]\n"
285
               "                       Query or set reboot-to-firmware EFI flag\n"
286
               "\n%3$sBoot Loader Specification Commands:%4$s\n"
287
               "  list                 List boot loader entries\n"
288
               "  unlink ID            Remove boot loader entry\n"
289
               "  cleanup              Remove files in ESP not referenced in any boot entry\n"
290
               "\n%3$sBoot Loader Interface Commands:%4$s\n"
291
               "  set-default ID       Set default boot loader entry\n"
292
               "  set-oneshot ID       Set default boot loader entry, for next boot only\n"
293
               "  set-sysfail ID       Set boot loader entry used in case of a system failure\n"
294
               "  set-timeout SECONDS  Set the menu timeout\n"
295
               "  set-timeout-oneshot SECONDS\n"
296
               "                       Set the menu timeout for the next boot only\n"
297
               "\n%3$ssystemd-boot Commands:%4$s\n"
298
               "  install              Install systemd-boot to the ESP and EFI variables\n"
299
               "  update               Update systemd-boot in the ESP and EFI variables\n"
300
               "  remove               Remove systemd-boot from the ESP and EFI variables\n"
301
               "  is-installed         Test whether systemd-boot is installed in the ESP\n"
302
               "  random-seed          Initialize or refresh random seed in ESP and EFI\n"
303
               "                       variables\n"
304
               "\n%3$sKernel Image Commands:%4$s\n"
305
               "  kernel-identify KERNEL-IMAGE\n"
306
               "                       Identify kernel image type\n"
307
               "  kernel-inspect KERNEL-IMAGE\n"
308
               "                       Prints details about the kernel image\n"
309
               "\n%3$sBlock Device Discovery Commands:%4$s\n"
310
               "  -p --print-esp-path  Print path to the EFI System Partition mount point\n"
311
               "  -x --print-boot-path Print path to the $BOOT partition mount point\n"
312
               "     --print-loader-path\n"
313
               "                       Print path to currently booted boot loader binary\n"
314
               "     --print-stub-path Print path to currently booted unified kernel binary\n"
315
               "  -R --print-root-device\n"
316
               "                       Print path to the block device node backing the\n"
317
               "                       root file system (returns e.g. /dev/nvme0n1p5)\n"
318
               "  -RR                  Print path to the whole disk block device node\n"
319
               "                       backing the root FS (returns e.g. /dev/nvme0n1)\n"
320
               "\n%3$sOptions:%4$s\n"
321
               "  -h --help            Show this help\n"
322
               "     --version         Print version\n"
323
               "     --esp-path=PATH   Path to the EFI System Partition (ESP)\n"
324
               "     --boot-path=PATH  Path to the $BOOT partition\n"
325
               "     --root=PATH       Operate on an alternate filesystem root\n"
326
               "     --image=PATH      Operate on disk image as filesystem root\n"
327
               "     --image-policy=POLICY\n"
328
               "                       Specify disk image dissection policy\n"
329
               "     --install-source=auto|image|host\n"
330
               "                       Where to pick files when using --root=/--image=\n"
331
               "     --variables=yes|no\n"
332
               "                       Whether to modify EFI variables\n"
333
               "     --random-seed=yes|no\n"
334
               "                       Whether to create random-seed file during install\n"
335
               "     --no-pager        Do not pipe output into a pager\n"
336
               "     --graceful        Don't fail when the ESP cannot be found or EFI\n"
337
               "                       variables cannot be written\n"
338
               "  -q --quiet           Suppress output\n"
339
               "     --make-entry-directory=yes|no|auto\n"
340
               "                       Create $BOOT/ENTRY-TOKEN/ directory\n"
341
               "     --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
342
               "                       Entry token to use for this installation\n"
343
               "     --json=pretty|short|off\n"
344
               "                       Generate JSON output\n"
345
               "     --all-architectures\n"
346
               "                       Install all supported EFI architectures\n"
347
               "     --efi-boot-option-description=DESCRIPTION\n"
348
               "                       Description of the entry in the boot option list\n"
349
               "     --efi-boot-option-description-with-device=yes\n"
350
               "                       Suffix description with disk vendor/model/serial\n"
351
               "     --dry-run         Dry run (unlink and cleanup)\n"
352
               "     --secure-boot-auto-enroll=yes|no\n"
353
               "                       Set up secure boot auto-enrollment\n"
354
               "     --private-key=PATH|URI\n"
355
               "                       Private key to use when setting up secure boot\n"
356
               "                       auto-enrollment or an engine or provider specific\n"
357
               "                       designation if --private-key-source= is used\n"
358
               "     --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
359
               "                       Specify how to use KEY for --private-key=. Allows\n"
360
               "                       an OpenSSL engine/provider to be used when setting\n"
361
               "                       up secure boot auto-enrollment\n"
362
               "     --certificate=PATH|URI\n"
363
               "                       PEM certificate to use when setting up Secure Boot\n"
364
               "                       auto-enrollment, or a provider specific designation\n"
365
               "                       if --certificate-source= is used\n"
366
               "     --certificate-source=file|provider:PROVIDER\n"
367
               "                       Specify how to interpret the certificate from\n"
368
               "                       --certificate=. Allows the certificate to be loaded\n"
369
               "                       from an OpenSSL provider\n"
370
               "\nSee the %2$s for details.\n",
371
               program_invocation_short_name,
372
               link,
373
               ansi_underline(),
374
               ansi_normal(),
375
               ansi_highlight(),
376
               ansi_normal());
377

378
        return 0;
379
}
380

381
static int parse_argv(int argc, char *argv[]) {
298✔
382
        enum {
298✔
383
                ARG_ESP_PATH = 0x100,
384
                ARG_BOOT_PATH,
385
                ARG_ROOT,
386
                ARG_IMAGE,
387
                ARG_IMAGE_POLICY,
388
                ARG_INSTALL_SOURCE,
389
                ARG_VERSION,
390
                ARG_VARIABLES,
391
                ARG_NO_VARIABLES,
392
                ARG_RANDOM_SEED,
393
                ARG_NO_PAGER,
394
                ARG_GRACEFUL,
395
                ARG_MAKE_ENTRY_DIRECTORY,
396
                ARG_ENTRY_TOKEN,
397
                ARG_JSON,
398
                ARG_ARCH_ALL,
399
                ARG_EFI_BOOT_OPTION_DESCRIPTION,
400
                ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE,
401
                ARG_DRY_RUN,
402
                ARG_PRINT_LOADER_PATH,
403
                ARG_PRINT_STUB_PATH,
404
                ARG_SECURE_BOOT_AUTO_ENROLL,
405
                ARG_CERTIFICATE,
406
                ARG_CERTIFICATE_SOURCE,
407
                ARG_PRIVATE_KEY,
408
                ARG_PRIVATE_KEY_SOURCE,
409
        };
410

411
        static const struct option options[] = {
298✔
412
                { "help",                                    no_argument,       NULL, 'h'                                         },
413
                { "version",                                 no_argument,       NULL, ARG_VERSION                                 },
414
                { "esp-path",                                required_argument, NULL, ARG_ESP_PATH                                },
415
                { "path",                                    required_argument, NULL, ARG_ESP_PATH                                }, /* Compatibility alias */
416
                { "boot-path",                               required_argument, NULL, ARG_BOOT_PATH                               },
417
                { "root",                                    required_argument, NULL, ARG_ROOT                                    },
418
                { "image",                                   required_argument, NULL, ARG_IMAGE                                   },
419
                { "image-policy",                            required_argument, NULL, ARG_IMAGE_POLICY                            },
420
                { "install-source",                          required_argument, NULL, ARG_INSTALL_SOURCE                          },
421
                { "print-esp-path",                          no_argument,       NULL, 'p'                                         },
422
                { "print-path",                              no_argument,       NULL, 'p'                                         }, /* Compatibility alias */
423
                { "print-boot-path",                         no_argument,       NULL, 'x'                                         },
424
                { "print-loader-path",                       no_argument,       NULL, ARG_PRINT_LOADER_PATH                       },
425
                { "print-stub-path",                         no_argument,       NULL, ARG_PRINT_STUB_PATH                         },
426
                { "print-root-device",                       no_argument,       NULL, 'R'                                         },
427
                { "variables",                               required_argument, NULL, ARG_VARIABLES                               },
428
                { "no-variables",                            no_argument,       NULL, ARG_NO_VARIABLES                            }, /* Compatibility alias */
429
                { "random-seed",                             required_argument, NULL, ARG_RANDOM_SEED                             },
430
                { "no-pager",                                no_argument,       NULL, ARG_NO_PAGER                                },
431
                { "graceful",                                no_argument,       NULL, ARG_GRACEFUL                                },
432
                { "quiet",                                   no_argument,       NULL, 'q'                                         },
433
                { "make-entry-directory",                    required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY                    },
434
                { "make-machine-id-directory",               required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY                    }, /* Compatibility alias */
435
                { "entry-token",                             required_argument, NULL, ARG_ENTRY_TOKEN                             },
436
                { "json",                                    required_argument, NULL, ARG_JSON                                    },
437
                { "all-architectures",                       no_argument,       NULL, ARG_ARCH_ALL                                },
438
                { "efi-boot-option-description",             required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION             },
439
                { "efi-boot-option-description-with-device", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE },
440
                { "dry-run",                                 no_argument,       NULL, ARG_DRY_RUN                                 },
441
                { "secure-boot-auto-enroll",                 required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL                 },
442
                { "certificate",                             required_argument, NULL, ARG_CERTIFICATE                             },
443
                { "certificate-source",                      required_argument, NULL, ARG_CERTIFICATE_SOURCE                      },
444
                { "private-key",                             required_argument, NULL, ARG_PRIVATE_KEY                             },
445
                { "private-key-source",                      required_argument, NULL, ARG_PRIVATE_KEY_SOURCE                      },
446
                {}
447
        };
448

449
        int c, r;
298✔
450

451
        assert(argc >= 0);
298✔
452
        assert(argv);
298✔
453

454
        while ((c = getopt_long(argc, argv, "hpxRq", options, NULL)) >= 0)
729✔
455
                switch (c) {
437✔
456

457
                case 'h':
3✔
458
                        help(0, NULL, NULL);
3✔
459
                        return 0;
3✔
460

461
                case ARG_VERSION:
3✔
462
                        return version();
3✔
463

464
                case ARG_ESP_PATH:
3✔
465
                        r = free_and_strdup(&arg_esp_path, optarg);
3✔
466
                        if (r < 0)
3✔
467
                                return log_oom();
×
468
                        break;
469

470
                case ARG_BOOT_PATH:
3✔
471
                        r = free_and_strdup(&arg_xbootldr_path, optarg);
3✔
472
                        if (r < 0)
3✔
473
                                return log_oom();
×
474
                        break;
475

476
                case ARG_ROOT:
34✔
477
                        r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
34✔
478
                        if (r < 0)
34✔
479
                                return r;
480
                        break;
481

482
                case ARG_IMAGE:
29✔
483
                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
29✔
484
                        if (r < 0)
29✔
485
                                return r;
486
                        break;
487

488
                case ARG_IMAGE_POLICY:
×
489
                        r = parse_image_policy_argument(optarg, &arg_image_policy);
×
490
                        if (r < 0)
×
491
                                return r;
492
                        break;
493

494
                case ARG_INSTALL_SOURCE: {
×
495
                        InstallSource is = install_source_from_string(optarg);
×
496
                        if (is < 0)
×
497
                                return log_error_errno(is, "Unexpected parameter for --install-source=: %s", optarg);
×
498

499
                        arg_install_source = is;
×
500
                        break;
×
501
                }
502

503
                case 'p':
47✔
504
                        arg_print_esp_path = true;
47✔
505
                        break;
47✔
506

507
                case 'x':
8✔
508
                        arg_print_dollar_boot_path = true;
8✔
509
                        break;
8✔
510

511
                case ARG_PRINT_LOADER_PATH:
×
512
                        arg_print_loader_path = true;
×
513
                        break;
×
514

515
                case ARG_PRINT_STUB_PATH:
6✔
516
                        arg_print_stub_path = true;
6✔
517
                        break;
6✔
518

519
                case 'R':
7✔
520
                        arg_print_root_device++;
7✔
521
                        break;
7✔
522

523
                case ARG_VARIABLES:
118✔
524
                        r = parse_tristate_argument("--variables=", optarg, &arg_touch_variables);
118✔
525
                        if (r < 0)
118✔
526
                                return r;
527
                        break;
528

529
                case ARG_NO_VARIABLES:
×
530
                        arg_touch_variables = false;
×
531
                        break;
×
532

533
                case ARG_RANDOM_SEED:
×
534
                        r = parse_boolean_argument("--random-seed=", optarg, &arg_install_random_seed);
×
535
                        if (r < 0)
×
536
                                return r;
537
                        break;
538

539
                case ARG_NO_PAGER:
×
540
                        arg_pager_flags |= PAGER_DISABLE;
×
541
                        break;
×
542

543
                case ARG_GRACEFUL:
136✔
544
                        _arg_graceful = ARG_GRACEFUL_YES;
136✔
545
                        break;
136✔
546

547
                case 'q':
6✔
548
                        arg_quiet = true;
6✔
549
                        break;
6✔
550

551
                case ARG_ENTRY_TOKEN:
×
552
                        r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token);
×
553
                        if (r < 0)
×
554
                                return r;
555
                        break;
556

557
                case ARG_MAKE_ENTRY_DIRECTORY:
13✔
558
                        if (streq(optarg, "auto"))  /* retained for backwards compatibility */
13✔
559
                                arg_make_entry_directory = -1; /* yes if machine-id is permanent */
×
560
                        else {
561
                                r = parse_boolean_argument("--make-entry-directory=", optarg, NULL);
13✔
562
                                if (r < 0)
13✔
563
                                        return r;
564

565
                                arg_make_entry_directory = r;
13✔
566
                        }
567
                        break;
568

569
                case ARG_JSON:
6✔
570
                        r = parse_json_argument(optarg, &arg_json_format_flags);
6✔
571
                        if (r <= 0)
6✔
572
                                return r;
573
                        break;
574

575
                case ARG_ARCH_ALL:
12✔
576
                        arg_arch_all = true;
12✔
577
                        break;
12✔
578

579
                case ARG_EFI_BOOT_OPTION_DESCRIPTION:
×
580
                        if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) {
×
581
                                _cleanup_free_ char *escaped = cescape(optarg);
×
582
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
583
                                                       "Invalid --efi-boot-option-description=: %s", strna(escaped));
584
                        }
585
                        if (strlen(optarg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
×
586
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
587
                                                       "--efi-boot-option-description= too long: %zu > %zu",
588
                                                       strlen(optarg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
589
                        r = free_and_strdup_warn(&arg_efi_boot_option_description, optarg);
×
590
                        if (r < 0)
×
591
                                return r;
592
                        break;
593

594
                case ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE:
×
595
                        r = parse_boolean_argument("--efi-boot-option-description-with-device=", optarg, &arg_efi_boot_option_description_with_device);
×
596
                        if (r < 0)
×
597
                                return r;
598

599
                        break;
600

601
                case ARG_DRY_RUN:
×
602
                        arg_dry_run = true;
×
603
                        break;
×
604

605
                case ARG_SECURE_BOOT_AUTO_ENROLL:
1✔
606
                        r = parse_boolean_argument("--secure-boot-auto-enroll=", optarg, &arg_secure_boot_auto_enroll);
1✔
607
                        if (r < 0)
1✔
608
                                return r;
609
                        break;
610

611
                case ARG_CERTIFICATE:
1✔
612
                        r = free_and_strdup_warn(&arg_certificate, optarg);
1✔
613
                        if (r < 0)
1✔
614
                                return r;
615
                        break;
616

617
                case ARG_CERTIFICATE_SOURCE:
×
618
                        r = parse_openssl_certificate_source_argument(
×
619
                                        optarg,
620
                                        &arg_certificate_source,
621
                                        &arg_certificate_source_type);
622
                        if (r < 0)
×
623
                                return r;
624
                        break;
625

626
                case ARG_PRIVATE_KEY: {
1✔
627
                        r = free_and_strdup_warn(&arg_private_key, optarg);
1✔
628
                        if (r < 0)
1✔
629
                                return r;
630
                        break;
631
                }
632

633
                case ARG_PRIVATE_KEY_SOURCE:
×
634
                        r = parse_openssl_key_source_argument(
×
635
                                        optarg,
636
                                        &arg_private_key_source,
637
                                        &arg_private_key_source_type);
638
                        if (r < 0)
×
639
                                return r;
640
                        break;
641

642
                case '?':
643
                        return -EINVAL;
644

645
                default:
×
646
                        assert_not_reached();
×
647
                }
648

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

653
        if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
292✔
654
                        "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
655
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
656
                                       "Options --root= and --image= are not supported with verb %s.",
657
                                       argv[optind]);
658

659
        if (arg_root && arg_image)
292✔
660
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
×
661

662
        if (arg_install_source != INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
292✔
663
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
×
664

665
        if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
292✔
666
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry-run is only supported with --unlink or --cleanup");
×
667

668
        if (arg_secure_boot_auto_enroll) {
292✔
669
#if HAVE_OPENSSL
670
                if (!arg_certificate)
1✔
671
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided.");
×
672

673
                if (!arg_private_key)
1✔
674
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided.");
×
675
#else
676
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Secure boot auto-enrollment requested but OpenSSL support is disabled.");
677
#endif
678
        }
679

680
        r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
292✔
681
        if (r < 0)
292✔
682
                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
×
683
        if (r > 0) {
292✔
684
                arg_varlink = true;
15✔
685
                arg_pager_flags |= PAGER_DISABLE;
15✔
686
        }
687

688
        return 1;
689
}
690

691
static int bootctl_main(int argc, char *argv[]) {
266✔
692
        static const Verb verbs[] = {
266✔
693
                { "help",                VERB_ANY, VERB_ANY, 0,            help                     },
694
                { "status",              VERB_ANY, 1,        VERB_DEFAULT, verb_status              },
695
                { "install",             VERB_ANY, 1,        0,            verb_install             },
696
                { "update",              VERB_ANY, 1,        0,            verb_install             },
697
                { "remove",              VERB_ANY, 1,        0,            verb_remove              },
698
                { "is-installed",        VERB_ANY, 1,        0,            verb_is_installed        },
699
                { "kernel-identify",     2,        2,        0,            verb_kernel_identify     },
700
                { "kernel-inspect",      2,        2,        0,            verb_kernel_inspect      },
701
                { "list",                VERB_ANY, 1,        0,            verb_list                },
702
                { "unlink",              2,        2,        0,            verb_unlink              },
703
                { "cleanup",             VERB_ANY, 1,        0,            verb_cleanup             },
704
                { "set-default",         2,        2,        0,            verb_set_efivar          },
705
                { "set-preferred",       2,        2,        0,            verb_set_efivar          },
706
                { "set-oneshot",         2,        2,        0,            verb_set_efivar          },
707
                { "set-timeout",         2,        2,        0,            verb_set_efivar          },
708
                { "set-timeout-oneshot", 2,        2,        0,            verb_set_efivar          },
709
                { "set-sysfail",         2,        2,        0,            verb_set_efivar          },
710
                { "random-seed",         VERB_ANY, 1,        0,            verb_random_seed         },
711
                { "reboot-to-firmware",  VERB_ANY, 2,        0,            verb_reboot_to_firmware  },
712
                {}
713
        };
714

715
        return dispatch_verb(argc, argv, verbs, NULL);
266✔
716
}
717

718
static int vl_server(void) {
15✔
719
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
15✔
720
        int r;
15✔
721

722
        /* Invocation as Varlink service */
723

724
        r = varlink_server_new(
15✔
725
                        &varlink_server,
726
                        SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
727
                        /* userdata= */ NULL);
728
        if (r < 0)
15✔
UNCOV
729
                return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
730

731
        r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl);
15✔
732
        if (r < 0)
15✔
UNCOV
733
                return log_error_errno(r, "Failed to add Varlink interface: %m");
×
734

735
        r = sd_varlink_server_bind_method_many(
15✔
736
                        varlink_server,
737
                        "io.systemd.BootControl.ListBootEntries",     vl_method_list_boot_entries,
738
                        "io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
739
                        "io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware,
740
                        "io.systemd.BootControl.Install",             vl_method_install);
741
        if (r < 0)
15✔
UNCOV
742
                return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
743

744
        r = sd_varlink_server_loop_auto(varlink_server);
15✔
745
        if (r < 0)
15✔
UNCOV
746
                return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
747

748
        return 0;
749
}
750

751
static int run(int argc, char *argv[]) {
298✔
752
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
298✔
753
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
298✔
754
        int r;
298✔
755

756
        log_setup();
298✔
757

758
        r = parse_argv(argc, argv);
298✔
759
        if (r <= 0)
298✔
760
                return r;
761

762
        if (arg_varlink)
292✔
763
                return vl_server();
15✔
764

765
        if (arg_print_root_device > 0) {
277✔
766
                _cleanup_free_ char *path = NULL;
5✔
767
                dev_t devno;
5✔
768

769
                r = blockdev_get_root(LOG_ERR, &devno);
5✔
770
                if (r < 0)
5✔
771
                        return r;
772
                if (r == 0) {
5✔
773
                        log_error("Root file system not backed by a (single) whole block device.");
×
UNCOV
774
                        return 80; /* some recognizable error code */
×
775
                }
776

777
                if (arg_print_root_device > 1) {
5✔
778
                        r = block_get_whole_disk(devno, &devno);
2✔
779
                        if (r < 0)
2✔
UNCOV
780
                                log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m");
×
781
                }
782

783
                r = device_path_make_canonical(S_IFBLK, devno, &path);
5✔
784
                if (r < 0)
5✔
UNCOV
785
                        return log_error_errno(r,
×
786
                                               "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
787
                                               DEVNUM_FORMAT_VAL(devno));
788

789
                puts(path);
5✔
790
                return 0;
791
        }
792

793
        if (arg_print_loader_path || arg_print_stub_path)
272✔
794
                return print_loader_or_stub_path();
6✔
795

796
        /* Open up and mount the image */
797
        if (arg_image) {
266✔
798
                assert(!arg_root);
26✔
799

800
                r = mount_image_privately_interactively(
26✔
801
                                arg_image,
802
                                arg_image_policy,
803
                                DISSECT_IMAGE_GENERIC_ROOT |
804
                                DISSECT_IMAGE_USR_NO_ROOT |
805
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
806
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
807
                                &mounted_dir,
808
                                /* ret_dir_fd= */ NULL,
809
                                &loop_device);
810
                if (r < 0)
26✔
811
                        return r;
812

813
                arg_root = strdup(mounted_dir);
26✔
814
                if (!arg_root)
26✔
UNCOV
815
                        return log_oom();
×
816
        }
817

818
        return bootctl_main(argc, argv);
266✔
819
}
820

821
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
298✔
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