1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "bootctl-status.h"
8 #include "bootctl-util.h"
11 #include "devnum-util.h"
12 #include "dirent-util.h"
14 #include "efi-loader.h"
15 #include "errno-util.h"
19 #include "path-util.h"
20 #include "pretty-print.h"
21 #include "recurse-dir.h"
22 #include "terminal-util.h"
23 #include "tpm2-util.h"
25 static int boot_config_load_and_select(
29 const char *xbootldr_path
,
30 dev_t xbootldr_devid
) {
34 /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
35 * find the same entries twice. */
36 bool same
= esp_path
&& xbootldr_path
&& devnum_set_and_equal(esp_devid
, xbootldr_devid
);
38 r
= boot_config_load(config
, esp_path
, same
? NULL
: xbootldr_path
);
43 _cleanup_strv_free_
char **efi_entries
= NULL
;
45 r
= efi_loader_get_entries(&efi_entries
);
46 if (r
== -ENOENT
|| ERRNO_IS_NEG_NOT_SUPPORTED(r
))
47 log_debug_errno(r
, "Boot loader reported no entries.");
49 log_warning_errno(r
, "Failed to determine entries reported by boot loader, ignoring: %m");
51 (void) boot_config_augment_from_loader(config
, efi_entries
, /* only_auto= */ false);
54 return boot_config_select_special_entries(config
, /* skip_efivars= */ !!arg_root
);
57 static int status_entries(
58 const BootConfig
*config
,
60 sd_id128_t esp_partition_uuid
,
61 const char *xbootldr_path
,
62 sd_id128_t xbootldr_partition_uuid
) {
64 sd_id128_t dollar_boot_partition_uuid
;
65 const char *dollar_boot_path
;
69 assert(esp_path
|| xbootldr_path
);
72 dollar_boot_path
= xbootldr_path
;
73 dollar_boot_partition_uuid
= xbootldr_partition_uuid
;
75 dollar_boot_path
= esp_path
;
76 dollar_boot_partition_uuid
= esp_partition_uuid
;
79 printf("%sBoot Loader Entries:%s\n"
80 " $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path
);
81 if (!sd_id128_is_null(dollar_boot_partition_uuid
))
82 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")",
83 SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid
));
84 if (settle_entry_token() >= 0)
85 printf("\n token: %s", arg_entry_token
);
88 if (config
->default_entry
< 0)
89 printf("%zu entries, no entry could be determined as default.\n", config
->n_entries
);
91 printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
94 boot_config_default_entry(config
),
95 &config
->global_addons
,
96 /* show_as_default= */ false,
97 /* show_as_selected= */ false,
98 /* show_discovered= */ false);
100 /* < 0 is already logged by the function itself, let's just emit an extra warning if
101 the default entry is broken */
102 printf("\nWARNING: default boot entry is broken\n");
108 static int print_efi_option(uint16_t id
, int *n_printed
, bool in_order
) {
109 _cleanup_free_
char *title
= NULL
;
110 _cleanup_free_
char *path
= NULL
;
111 sd_id128_t partition
;
117 r
= efi_get_boot_option(id
, &title
, &partition
, &path
, &active
);
119 log_debug_errno(r
, "Boot option 0x%04X referenced but missing, ignoring: %m", id
);
123 return log_error_errno(r
, "Failed to read boot option 0x%04X: %m", id
);
125 /* print only configured entries with partition information */
126 if (!path
|| sd_id128_is_null(partition
)) {
127 log_debug("Ignoring boot entry 0x%04X without partition information.", id
);
131 efi_tilt_backslashes(path
);
133 if (*n_printed
== 0) /* Print section title before first entry */
134 printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
136 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title
), ansi_normal());
137 printf(" ID: 0x%04X\n", id
);
138 printf(" Status: %sactive%s\n", active
? "" : "in", in_order
? ", boot-order" : "");
139 printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
140 SD_ID128_FORMAT_VAL(partition
));
141 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), path
);
148 static int status_variables(void) {
149 _cleanup_free_
uint16_t *options
= NULL
, *order
= NULL
;
150 int n_options
, n_order
, n_printed
= 0;
152 n_options
= efi_get_boot_options(&options
);
153 if (n_options
== -ENOENT
)
154 return log_error_errno(n_options
,
155 "Failed to access EFI variables, efivarfs"
156 " needs to be available at /sys/firmware/efi/efivars/.");
158 return log_error_errno(n_options
, "Failed to read EFI boot entries: %m");
160 n_order
= efi_get_boot_order(&order
);
161 if (n_order
== -ENOENT
)
163 else if (n_order
< 0)
164 return log_error_errno(n_order
, "Failed to read EFI boot order: %m");
166 /* print entries in BootOrder first */
167 for (int i
= 0; i
< n_order
; i
++)
168 (void) print_efi_option(order
[i
], &n_printed
, /* in_order= */ true);
170 /* print remaining entries */
171 for (int i
= 0; i
< n_options
; i
++) {
172 for (int j
= 0; j
< n_order
; j
++)
173 if (options
[i
] == order
[j
])
176 (void) print_efi_option(options
[i
], &n_printed
, /* in_order= */ false);
183 printf("No boot loaders listed in EFI Variables.\n\n");
188 static int enumerate_binaries(
189 const char *esp_path
,
194 _cleanup_closedir_
DIR *d
= NULL
;
195 _cleanup_free_
char *p
= NULL
;
203 r
= chase_and_opendir(path
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
207 return log_error_errno(r
, "Failed to read \"%s/%s\": %m", esp_path
, path
);
209 FOREACH_DIRENT(de
, d
, break) {
210 _cleanup_free_
char *v
= NULL
, *filename
= NULL
;
211 _cleanup_close_
int fd
= -EBADF
;
213 if (!endswith_no_case(de
->d_name
, ".efi"))
216 filename
= path_join(p
, de
->d_name
);
219 LOG_SET_PREFIX(filename
);
221 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
223 return log_error_errno(errno
, "Failed to open file for reading: %m");
225 r
= get_file_version(fd
, &v
);
227 if (r
< 0 && r
!= -ESRCH
)
230 if (*previous
) { /* Let's output the previous entry now, since now we know that there will be
231 * one more, and can draw the tree glyph properly. */
233 *is_first
? "File:" : " ",
234 special_glyph(SPECIAL_GLYPH_TREE_BRANCH
), *previous
);
236 *previous
= mfree(*previous
);
239 /* Do not output this entry immediately, but store what should be printed in a state
240 * variable, because we only will know the tree glyph to print (branch or final edge) once we
241 * read one more entry */
242 if (r
== -ESRCH
) /* No systemd-owned file but still interesting to print */
243 r
= asprintf(previous
, "/%s/%s", path
, de
->d_name
);
244 else /* if (r >= 0) */
245 r
= asprintf(previous
, "/%s/%s (%s%s%s)", path
, de
->d_name
, ansi_highlight(), v
, ansi_normal());
255 static int status_binaries(const char *esp_path
, sd_id128_t partition
) {
256 _cleanup_free_
char *last
= NULL
;
257 bool is_first
= true;
260 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
263 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
267 printf(" ESP: %s", esp_path
);
268 if (!sd_id128_is_null(partition
))
269 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")", SD_ID128_FORMAT_VAL(partition
));
272 r
= enumerate_binaries(esp_path
, "EFI/systemd", &last
, &is_first
);
276 k
= enumerate_binaries(esp_path
, "EFI/BOOT", &last
, &is_first
);
282 if (last
) /* let's output the last entry now, since now we know that there will be no more, and can draw the tree glyph properly */
284 is_first
? "File:" : " ",
285 special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), last
);
287 if (r
== 0 && !arg_quiet
)
288 log_info("systemd-boot not installed in ESP.");
289 if (k
== 0 && !arg_quiet
)
290 log_info("No default/fallback boot loader installed in ESP.");
297 printf(" File: (can't access %s: %m)\n\n", esp_path
);
301 static void read_efi_var(const char *variable
, char **ret
) {
304 r
= efi_get_variable_string(variable
, ret
);
305 if (r
< 0 && r
!= -ENOENT
)
306 log_warning_errno(r
, "Failed to read EFI variable %s: %m", variable
);
309 static void print_yes_no_line(bool first
, bool good
, const char *name
) {
311 first
? " Features: " : " ",
312 COLOR_MARK_BOOL(good
),
316 int verb_status(int argc
, char *argv
[], void *userdata
) {
317 sd_id128_t esp_uuid
= SD_ID128_NULL
, xbootldr_uuid
= SD_ID128_NULL
;
318 dev_t esp_devid
= 0, xbootldr_devid
= 0;
321 r
= acquire_esp(/* unprivileged_mode= */ -1,
322 /* graceful= */ false,
323 /* ret_part= */ NULL
,
324 /* ret_pstart= */ NULL
,
325 /* ret_psize= */ NULL
,
328 if (arg_print_esp_path
) {
329 if (r
== -EACCES
) /* If we couldn't acquire the ESP path, log about access errors (which is the only
330 * error the find_esp_and_warn() won't log on its own) */
331 return log_error_errno(r
, "Failed to determine ESP location: %m");
339 r
= acquire_xbootldr(
340 /* unprivileged_mode= */ -1,
343 if (arg_print_dollar_boot_path
) {
345 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
349 const char *path
= arg_dollar_boot_path();
351 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Failed to determine XBOOTLDR location: %m");
357 r
= 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
358 * show what we can show */
360 pager_open(arg_pager_flags
);
362 if (!arg_root
&& is_efi_boot()) {
363 static const struct {
367 { EFI_LOADER_FEATURE_BOOT_COUNTING
, "Boot counting" },
368 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT
, "Menu timeout control" },
369 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
, "One-shot menu timeout control" },
370 { EFI_LOADER_FEATURE_ENTRY_DEFAULT
, "Default entry control" },
371 { EFI_LOADER_FEATURE_ENTRY_ONESHOT
, "One-shot entry control" },
372 { EFI_LOADER_FEATURE_XBOOTLDR
, "Support for XBOOTLDR partition" },
373 { EFI_LOADER_FEATURE_RANDOM_SEED
, "Support for passing random seed to OS" },
374 { EFI_LOADER_FEATURE_LOAD_DRIVER
, "Load drop-in drivers" },
375 { EFI_LOADER_FEATURE_SORT_KEY
, "Support Type #1 sort-key field" },
376 { EFI_LOADER_FEATURE_SAVED_ENTRY
, "Support @saved pseudo-entry" },
377 { EFI_LOADER_FEATURE_DEVICETREE
, "Support Type #1 devicetree field" },
378 { EFI_LOADER_FEATURE_SECUREBOOT_ENROLL
, "Enroll SecureBoot keys" },
379 { EFI_LOADER_FEATURE_RETAIN_SHIM
, "Retain SHIM protocols" },
380 { EFI_LOADER_FEATURE_MENU_DISABLE
, "Menu can be disabled" },
382 static const struct {
386 { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION
, "Stub sets ESP information" },
387 { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS
, "Picks up credentials from boot partition" },
388 { EFI_STUB_FEATURE_PICK_UP_SYSEXTS
, "Picks up system extension images from boot partition" },
389 { EFI_STUB_FEATURE_PICK_UP_CONFEXTS
, "Picks up configuration extension images from boot partition" },
390 { EFI_STUB_FEATURE_THREE_PCRS
, "Measures kernel+command line+sysexts" },
391 { EFI_STUB_FEATURE_RANDOM_SEED
, "Support for passing random seed to OS" },
392 { EFI_STUB_FEATURE_CMDLINE_ADDONS
, "Pick up .cmdline from addons" },
393 { EFI_STUB_FEATURE_CMDLINE_SMBIOS
, "Pick up .cmdline from SMBIOS Type 11" },
394 { EFI_STUB_FEATURE_DEVICETREE_ADDONS
, "Pick up .dtb from addons" },
396 _cleanup_free_
char *fw_type
= NULL
, *fw_info
= NULL
, *loader
= NULL
, *loader_path
= NULL
, *stub
= NULL
;
397 sd_id128_t loader_part_uuid
= SD_ID128_NULL
;
398 uint64_t loader_features
= 0, stub_features
= 0;
402 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType
), &fw_type
);
403 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo
), &fw_info
);
404 read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo
), &loader
);
405 read_efi_var(EFI_LOADER_VARIABLE(StubInfo
), &stub
);
406 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier
), &loader_path
);
407 (void) efi_loader_get_features(&loader_features
);
408 (void) efi_stub_get_features(&stub_features
);
411 efi_tilt_backslashes(loader_path
);
413 k
= efi_loader_get_device_part_uuid(&loader_part_uuid
);
414 if (k
< 0 && k
!= -ENOENT
)
415 r
= log_warning_errno(k
, "Failed to read EFI variable LoaderDevicePartUUID: %m");
417 SecureBootMode secure
= efi_get_secure_boot_mode();
418 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
419 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type
), strna(fw_info
), ansi_normal());
420 printf(" Firmware Arch: %s\n", get_efi_arch());
421 printf(" Secure Boot: %s%s%s",
422 IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
) ? ansi_highlight_green() : ansi_normal(),
423 enabled_disabled(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)),
426 if (secure
!= SECURE_BOOT_DISABLED
)
427 printf(" (%s)\n", secure_boot_mode_to_string(secure
));
432 printf(" TPM2 Support: %s%s%s\n",
433 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? ansi_highlight_green() :
434 (s
& (TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
435 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? "yes" :
436 (s
& TPM2_SUPPORT_FIRMWARE
) ? "firmware only, driver unavailable" :
437 (s
& TPM2_SUPPORT_DRIVER
) ? "driver only, firmware unavailable" : "no",
440 k
= efi_measured_uki(LOG_DEBUG
);
442 printf(" Measured UKI: %syes%s\n", ansi_highlight_green(), ansi_normal());
444 printf(" Measured UKI: no\n");
447 printf(" Measured UKI: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
450 k
= efi_get_reboot_to_firmware();
452 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
454 printf(" Boot into FW: supported\n");
455 else if (k
== -EOPNOTSUPP
)
456 printf(" Boot into FW: not supported\n");
459 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
463 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
464 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader
), ansi_normal());
466 for (size_t i
= 0; i
< ELEMENTSOF(loader_flags
); i
++)
467 print_yes_no_line(i
== 0, FLAGS_SET(loader_features
, loader_flags
[i
].flag
), loader_flags
[i
].name
);
469 sd_id128_t bootloader_esp_uuid
;
470 bool have_bootloader_esp_uuid
= efi_loader_get_device_part_uuid(&bootloader_esp_uuid
) >= 0;
472 print_yes_no_line(false, have_bootloader_esp_uuid
, "Boot loader sets ESP information");
473 if (have_bootloader_esp_uuid
&& !sd_id128_is_null(esp_uuid
) &&
474 !sd_id128_equal(esp_uuid
, bootloader_esp_uuid
))
475 printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR
" vs. "SD_ID128_UUID_FORMAT_STR
")!\n",
476 SD_ID128_FORMAT_VAL(bootloader_esp_uuid
),
477 SD_ID128_FORMAT_VAL(esp_uuid
));
480 printf(" Stub: %s\n", stub
);
481 for (size_t i
= 0; i
< ELEMENTSOF(stub_flags
); i
++)
482 print_yes_no_line(i
== 0, FLAGS_SET(stub_features
, stub_flags
[i
].flag
), stub_flags
[i
].name
);
484 if (!sd_id128_is_null(loader_part_uuid
))
485 printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
486 SD_ID128_FORMAT_VAL(loader_part_uuid
));
488 printf(" ESP: n/a\n");
489 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), strna(loader_path
));
492 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
493 have
= access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken
)), F_OK
) >= 0;
494 printf(" System Token: %s\n", have
? "set" : "not set");
497 _cleanup_free_
char *p
= NULL
;
499 p
= path_join(arg_esp_path
, "/loader/random-seed");
503 have
= access(p
, F_OK
) >= 0;
504 printf(" Exists: %s\n", yes_no(have
));
509 printf("%sSystem:%s\n"
510 "Not booted with EFI\n\n",
511 ansi_underline(), ansi_normal());
514 RET_GATHER(r
, status_binaries(arg_esp_path
, esp_uuid
));
516 if (!arg_root
&& is_efi_boot())
517 RET_GATHER(r
, status_variables());
519 if (arg_esp_path
|| arg_xbootldr_path
) {
520 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
522 k
= boot_config_load_and_select(&config
,
523 arg_esp_path
, esp_devid
,
524 arg_xbootldr_path
, xbootldr_devid
);
529 status_entries(&config
,
530 arg_esp_path
, esp_uuid
,
531 arg_xbootldr_path
, xbootldr_uuid
));
537 static int ref_file(Hashmap
*known_files
, const char *fn
, int increment
) {
543 /* just gracefully ignore this. This way the caller doesn't
544 have to verify whether the bootloader entry is relevant */
548 n
= PTR_TO_INT(hashmap_get2(known_files
, fn
, (void**)&k
));
554 (void) hashmap_remove(known_files
, fn
);
557 _cleanup_free_
char *t
= NULL
;
562 r
= hashmap_put(known_files
, t
, INT_TO_PTR(n
));
567 r
= hashmap_update(known_files
, fn
, INT_TO_PTR(n
));
575 static void deref_unlink_file(Hashmap
*known_files
, const char *fn
, const char *root
) {
576 _cleanup_free_
char *path
= NULL
;
581 /* just gracefully ignore this. This way the caller doesn't
582 have to verify whether the bootloader entry is relevant */
586 r
= ref_file(known_files
, fn
, -1);
588 return (void) log_warning_errno(r
, "Failed to deref \"%s\", ignoring: %m", fn
);
593 r
= chase_and_access(fn
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, F_OK
, &path
);
595 log_info_errno(r
, "Unable to determine whether \"%s\" exists, ignoring: %m", fn
);
597 log_info("Would remove \"%s\"", path
);
601 r
= chase_and_unlink(fn
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, 0, &path
);
603 log_info("Removed \"%s\"", path
);
604 else if (r
!= -ENOENT
)
605 return (void) log_warning_errno(r
, "Failed to remove \"%s\", ignoring: %m", fn
);
607 _cleanup_free_
char *d
= NULL
;
608 if (path_extract_directory(fn
, &d
) >= 0 && !path_equal(d
, "/")) {
609 r
= chase_and_unlink(d
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, AT_REMOVEDIR
, NULL
);
610 if (r
< 0 && !IN_SET(r
, -ENOTEMPTY
, -ENOENT
))
611 log_warning_errno(r
, "Failed to remove directory \"%s\", ignoring: %m", d
);
615 static int count_known_files(const BootConfig
*config
, const char* root
, Hashmap
**ret_known_files
) {
616 _cleanup_(hashmap_free_free_keyp
) Hashmap
*known_files
= NULL
;
620 assert(ret_known_files
);
622 known_files
= hashmap_new(&path_hash_ops
);
626 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
627 const BootEntry
*e
= config
->entries
+ i
;
629 if (!path_equal(e
->root
, root
))
632 r
= ref_file(known_files
, e
->kernel
, +1);
635 r
= ref_file(known_files
, e
->efi
, +1);
638 STRV_FOREACH(s
, e
->initrd
) {
639 r
= ref_file(known_files
, *s
, +1);
643 r
= ref_file(known_files
, e
->device_tree
, +1);
646 STRV_FOREACH(s
, e
->device_tree_overlay
) {
647 r
= ref_file(known_files
, *s
, +1);
653 *ret_known_files
= TAKE_PTR(known_files
);
658 static int boot_config_find_in(const BootConfig
*config
, const char *root
, const char *id
) {
664 for (size_t i
= 0; i
< config
->n_entries
; i
++)
665 if (path_equal(config
->entries
[i
].root
, root
) &&
666 fnmatch(id
, config
->entries
[i
].id
, FNM_CASEFOLD
) == 0)
672 static int unlink_entry(const BootConfig
*config
, const char *root
, const char *id
) {
673 _cleanup_(hashmap_free_free_keyp
) Hashmap
*known_files
= NULL
;
674 const BootEntry
*e
= NULL
;
679 r
= count_known_files(config
, root
, &known_files
);
681 return log_error_errno(r
, "Failed to count files in %s: %m", root
);
683 r
= boot_config_find_in(config
, root
, id
);
687 if (r
== config
->default_entry
)
688 log_warning("%s is the default boot entry", id
);
689 if (r
== config
->selected_entry
)
690 log_warning("%s is the selected boot entry", id
);
692 e
= &config
->entries
[r
];
694 deref_unlink_file(known_files
, e
->kernel
, e
->root
);
695 deref_unlink_file(known_files
, e
->efi
, e
->root
);
696 STRV_FOREACH(s
, e
->initrd
)
697 deref_unlink_file(known_files
, *s
, e
->root
);
698 deref_unlink_file(known_files
, e
->device_tree
, e
->root
);
699 STRV_FOREACH(s
, e
->device_tree_overlay
)
700 deref_unlink_file(known_files
, *s
, e
->root
);
703 log_info("Would remove \"%s\"", e
->path
);
705 r
= chase_and_unlink(e
->path
, root
, CHASE_PROHIBIT_SYMLINKS
, 0, NULL
);
707 return log_error_errno(r
, "Failed to remove \"%s\": %m", e
->path
);
709 log_info("Removed %s", e
->path
);
715 static int list_remove_orphaned_file(
716 RecurseDirEvent event
,
720 const struct dirent
*de
,
721 const struct statx
*sx
,
724 Hashmap
*known_files
= userdata
;
729 if (event
!= RECURSE_DIR_ENTRY
)
730 return RECURSE_DIR_CONTINUE
;
732 if (hashmap_get(known_files
, path
))
733 return RECURSE_DIR_CONTINUE
; /* keep! */
736 log_info("Would remove %s", path
);
737 else if (unlinkat(dir_fd
, de
->d_name
, 0) < 0)
738 log_warning_errno(errno
, "Failed to remove \"%s\", ignoring: %m", path
);
740 log_info("Removed %s", path
);
742 return RECURSE_DIR_CONTINUE
;
745 static int cleanup_orphaned_files(
746 const BootConfig
*config
,
749 _cleanup_(hashmap_free_free_keyp
) Hashmap
*known_files
= NULL
;
750 _cleanup_free_
char *full
= NULL
, *p
= NULL
;
751 _cleanup_close_
int dir_fd
= -EBADF
;
757 log_info("Cleaning %s", root
);
759 r
= settle_entry_token();
763 r
= count_known_files(config
, root
, &known_files
);
765 return log_error_errno(r
, "Failed to count files in %s: %m", root
);
767 dir_fd
= chase_and_open(arg_entry_token
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
,
768 O_DIRECTORY
|O_CLOEXEC
, &full
);
769 if (dir_fd
== -ENOENT
)
772 return log_error_errno(dir_fd
, "Failed to open '%s/%s': %m", root
, arg_entry_token
);
774 p
= path_join("/", arg_entry_token
);
778 r
= recurse_dir(dir_fd
, p
, 0, UINT_MAX
, RECURSE_DIR_SORT
, list_remove_orphaned_file
, known_files
);
780 return log_error_errno(r
, "Failed to cleanup %s: %m", full
);
785 int verb_list(int argc
, char *argv
[], void *userdata
) {
786 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
787 dev_t esp_devid
= 0, xbootldr_devid
= 0;
790 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
791 * things: turn off logging about access errors and turn off potentially privileged device probing.
792 * Here we're interested in the latter but not the former, hence request the mode, and log about
795 r
= acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL
, NULL
, NULL
, NULL
, &esp_devid
);
796 if (r
== -EACCES
) /* We really need the ESP path for this call, hence also log about access errors */
797 return log_error_errno(r
, "Failed to determine ESP location: %m");
801 r
= acquire_xbootldr(/* unprivileged_mode= */ -1, NULL
, &xbootldr_devid
);
803 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
807 r
= boot_config_load_and_select(&config
, arg_esp_path
, esp_devid
, arg_xbootldr_path
, xbootldr_devid
);
811 if (config
.n_entries
== 0 && FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
812 log_info("No boot loader entries found.");
816 if (streq(argv
[0], "list")) {
817 pager_open(arg_pager_flags
);
818 return show_boot_entries(&config
, arg_json_format_flags
);
819 } else if (streq(argv
[0], "cleanup")) {
820 if (arg_xbootldr_path
&& xbootldr_devid
!= esp_devid
)
821 cleanup_orphaned_files(&config
, arg_xbootldr_path
);
822 return cleanup_orphaned_files(&config
, arg_esp_path
);
824 assert(streq(argv
[0], "unlink"));
825 if (arg_xbootldr_path
&& xbootldr_devid
!= esp_devid
) {
826 r
= unlink_entry(&config
, arg_xbootldr_path
, argv
[1]);
827 if (r
== 0 || r
!= -ENOENT
)
830 return unlink_entry(&config
, arg_esp_path
, argv
[1]);
834 int verb_unlink(int argc
, char *argv
[], void *userdata
) {
835 return verb_list(argc
, argv
, userdata
);
838 int vl_method_list_boot_entries(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
839 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
840 dev_t esp_devid
= 0, xbootldr_devid
= 0;
845 if (json_variant_elements(parameters
) > 0)
846 return varlink_error_invalid_parameter(link
, parameters
);
848 r
= acquire_esp(/* unprivileged_mode= */ false,
849 /* graceful= */ false,
850 /* ret_part= */ NULL
,
851 /* ret_pstart= */ NULL
,
852 /* ret_psize= */ NULL
,
855 if (r
== -EACCES
) /* We really need the ESP path for this call, hence also log about access errors */
856 return log_error_errno(r
, "Failed to determine ESP location: %m");
860 r
= acquire_xbootldr(
861 /* unprivileged_mode= */ false,
862 /* ret_uuid= */ NULL
,
865 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
869 r
= boot_config_load_and_select(&config
, arg_esp_path
, esp_devid
, arg_xbootldr_path
, xbootldr_devid
);
873 _cleanup_(json_variant_unrefp
) JsonVariant
*previous
= NULL
;
874 for (size_t i
= 0; i
< config
.n_entries
; i
++) {
876 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(
877 JSON_BUILD_PAIR_VARIANT("entry", previous
)));
881 previous
= json_variant_unref(previous
);
884 r
= boot_entry_to_json(&config
, i
, &previous
);
889 return varlink_replyb(link
, JSON_BUILD_OBJECT(
890 JSON_BUILD_PAIR_CONDITION(previous
, "entry", JSON_BUILD_VARIANT(previous
))));