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 /* show_as_default= */ false,
96 /* show_as_selected= */ false,
97 /* show_discovered= */ false);
99 /* < 0 is already logged by the function itself, let's just emit an extra warning if
100 the default entry is broken */
101 printf("\nWARNING: default boot entry is broken\n");
107 static int print_efi_option(uint16_t id
, int *n_printed
, bool in_order
) {
108 _cleanup_free_
char *title
= NULL
;
109 _cleanup_free_
char *path
= NULL
;
110 sd_id128_t partition
;
116 r
= efi_get_boot_option(id
, &title
, &partition
, &path
, &active
);
118 log_debug_errno(r
, "Boot option 0x%04X referenced but missing, ignoring: %m", id
);
122 return log_error_errno(r
, "Failed to read boot option 0x%04X: %m", id
);
124 /* print only configured entries with partition information */
125 if (!path
|| sd_id128_is_null(partition
)) {
126 log_debug("Ignoring boot entry 0x%04X without partition information.", id
);
130 efi_tilt_backslashes(path
);
132 if (*n_printed
== 0) /* Print section title before first entry */
133 printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
135 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title
), ansi_normal());
136 printf(" ID: 0x%04X\n", id
);
137 printf(" Status: %sactive%s\n", active
? "" : "in", in_order
? ", boot-order" : "");
138 printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
139 SD_ID128_FORMAT_VAL(partition
));
140 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), path
);
147 static int status_variables(void) {
148 _cleanup_free_
uint16_t *options
= NULL
, *order
= NULL
;
149 int n_options
, n_order
, n_printed
= 0;
151 n_options
= efi_get_boot_options(&options
);
152 if (n_options
== -ENOENT
)
153 return log_error_errno(n_options
,
154 "Failed to access EFI variables, efivarfs"
155 " needs to be available at /sys/firmware/efi/efivars/.");
157 return log_error_errno(n_options
, "Failed to read EFI boot entries: %m");
159 n_order
= efi_get_boot_order(&order
);
160 if (n_order
== -ENOENT
)
162 else if (n_order
< 0)
163 return log_error_errno(n_order
, "Failed to read EFI boot order: %m");
165 /* print entries in BootOrder first */
166 for (int i
= 0; i
< n_order
; i
++)
167 (void) print_efi_option(order
[i
], &n_printed
, /* in_order= */ true);
169 /* print remaining entries */
170 for (int i
= 0; i
< n_options
; i
++) {
171 for (int j
= 0; j
< n_order
; j
++)
172 if (options
[i
] == order
[j
])
175 (void) print_efi_option(options
[i
], &n_printed
, /* in_order= */ false);
182 printf("No boot loaders listed in EFI Variables.\n\n");
187 static int enumerate_binaries(
188 const char *esp_path
,
193 _cleanup_closedir_
DIR *d
= NULL
;
194 _cleanup_free_
char *p
= NULL
;
202 r
= chase_and_opendir(path
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
206 return log_error_errno(r
, "Failed to read \"%s/%s\": %m", esp_path
, path
);
208 FOREACH_DIRENT(de
, d
, break) {
209 _cleanup_free_
char *v
= NULL
, *filename
= NULL
;
210 _cleanup_close_
int fd
= -EBADF
;
212 if (!endswith_no_case(de
->d_name
, ".efi"))
215 filename
= path_join(p
, de
->d_name
);
218 LOG_SET_PREFIX(filename
);
220 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
222 return log_error_errno(errno
, "Failed to open file for reading: %m");
224 r
= get_file_version(fd
, &v
);
226 if (r
< 0 && r
!= -ESRCH
)
229 if (*previous
) { /* Let's output the previous entry now, since now we know that there will be
230 * one more, and can draw the tree glyph properly. */
232 *is_first
? "File:" : " ",
233 special_glyph(SPECIAL_GLYPH_TREE_BRANCH
), *previous
);
235 *previous
= mfree(*previous
);
238 /* Do not output this entry immediately, but store what should be printed in a state
239 * variable, because we only will know the tree glyph to print (branch or final edge) once we
240 * read one more entry */
241 if (r
== -ESRCH
) /* No systemd-owned file but still interesting to print */
242 r
= asprintf(previous
, "/%s/%s", path
, de
->d_name
);
243 else /* if (r >= 0) */
244 r
= asprintf(previous
, "/%s/%s (%s%s%s)", path
, de
->d_name
, ansi_highlight(), v
, ansi_normal());
254 static int status_binaries(const char *esp_path
, sd_id128_t partition
) {
255 _cleanup_free_
char *last
= NULL
;
256 bool is_first
= true;
259 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
262 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
266 printf(" ESP: %s", esp_path
);
267 if (!sd_id128_is_null(partition
))
268 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")", SD_ID128_FORMAT_VAL(partition
));
271 r
= enumerate_binaries(esp_path
, "EFI/systemd", &last
, &is_first
);
275 k
= enumerate_binaries(esp_path
, "EFI/BOOT", &last
, &is_first
);
281 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 */
283 is_first
? "File:" : " ",
284 special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), last
);
286 if (r
== 0 && !arg_quiet
)
287 log_info("systemd-boot not installed in ESP.");
288 if (k
== 0 && !arg_quiet
)
289 log_info("No default/fallback boot loader installed in ESP.");
296 printf(" File: (can't access %s: %m)\n\n", esp_path
);
300 static void read_efi_var(const char *variable
, char **ret
) {
303 r
= efi_get_variable_string(variable
, ret
);
304 if (r
< 0 && r
!= -ENOENT
)
305 log_warning_errno(r
, "Failed to read EFI variable %s: %m", variable
);
308 static void print_yes_no_line(bool first
, bool good
, const char *name
) {
310 first
? " Features: " : " ",
311 COLOR_MARK_BOOL(good
),
315 int verb_status(int argc
, char *argv
[], void *userdata
) {
316 sd_id128_t esp_uuid
= SD_ID128_NULL
, xbootldr_uuid
= SD_ID128_NULL
;
317 dev_t esp_devid
= 0, xbootldr_devid
= 0;
320 r
= acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL
, NULL
, NULL
, &esp_uuid
, &esp_devid
);
321 if (arg_print_esp_path
) {
322 if (r
== -EACCES
) /* If we couldn't acquire the ESP path, log about access errors (which is the only
323 * error the find_esp_and_warn() won't log on its own) */
324 return log_error_errno(r
, "Failed to determine ESP location: %m");
331 r
= acquire_xbootldr(/* unprivileged_mode= */ -1, &xbootldr_uuid
, &xbootldr_devid
);
332 if (arg_print_dollar_boot_path
) {
334 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
338 const char *path
= arg_dollar_boot_path();
340 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Failed to determine XBOOTLDR location: %m");
345 if (arg_print_esp_path
|| arg_print_dollar_boot_path
)
348 r
= 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
349 * show what we can show */
351 pager_open(arg_pager_flags
);
353 if (!arg_root
&& is_efi_boot()) {
354 static const struct {
358 { EFI_LOADER_FEATURE_BOOT_COUNTING
, "Boot counting" },
359 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT
, "Menu timeout control" },
360 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
, "One-shot menu timeout control" },
361 { EFI_LOADER_FEATURE_ENTRY_DEFAULT
, "Default entry control" },
362 { EFI_LOADER_FEATURE_ENTRY_ONESHOT
, "One-shot entry control" },
363 { EFI_LOADER_FEATURE_XBOOTLDR
, "Support for XBOOTLDR partition" },
364 { EFI_LOADER_FEATURE_RANDOM_SEED
, "Support for passing random seed to OS" },
365 { EFI_LOADER_FEATURE_LOAD_DRIVER
, "Load drop-in drivers" },
366 { EFI_LOADER_FEATURE_SORT_KEY
, "Support Type #1 sort-key field" },
367 { EFI_LOADER_FEATURE_SAVED_ENTRY
, "Support @saved pseudo-entry" },
368 { EFI_LOADER_FEATURE_DEVICETREE
, "Support Type #1 devicetree field" },
369 { EFI_LOADER_FEATURE_SECUREBOOT_ENROLL
, "Enroll SecureBoot keys" },
370 { EFI_LOADER_FEATURE_RETAIN_SHIM
, "Retain SHIM protocols" },
371 { EFI_LOADER_FEATURE_MENU_DISABLE
, "Menu can be disabled" },
373 static const struct {
377 { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION
, "Stub sets ESP information" },
378 { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS
, "Picks up credentials from boot partition" },
379 { EFI_STUB_FEATURE_PICK_UP_SYSEXTS
, "Picks up system extension images from boot partition" },
380 { EFI_STUB_FEATURE_PICK_UP_CONFEXTS
, "Picks up configuration extension images from boot partition" },
381 { EFI_STUB_FEATURE_THREE_PCRS
, "Measures kernel+command line+sysexts" },
382 { EFI_STUB_FEATURE_RANDOM_SEED
, "Support for passing random seed to OS" },
383 { EFI_STUB_FEATURE_CMDLINE_ADDONS
, "Pick up .cmdline from addons" },
384 { EFI_STUB_FEATURE_CMDLINE_SMBIOS
, "Pick up .cmdline from SMBIOS Type 11" },
385 { EFI_STUB_FEATURE_DEVICETREE_ADDONS
, "Pick up .dtb from addons" },
387 _cleanup_free_
char *fw_type
= NULL
, *fw_info
= NULL
, *loader
= NULL
, *loader_path
= NULL
, *stub
= NULL
;
388 sd_id128_t loader_part_uuid
= SD_ID128_NULL
;
389 uint64_t loader_features
= 0, stub_features
= 0;
393 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType
), &fw_type
);
394 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo
), &fw_info
);
395 read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo
), &loader
);
396 read_efi_var(EFI_LOADER_VARIABLE(StubInfo
), &stub
);
397 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier
), &loader_path
);
398 (void) efi_loader_get_features(&loader_features
);
399 (void) efi_stub_get_features(&stub_features
);
402 efi_tilt_backslashes(loader_path
);
404 k
= efi_loader_get_device_part_uuid(&loader_part_uuid
);
405 if (k
< 0 && k
!= -ENOENT
)
406 r
= log_warning_errno(k
, "Failed to read EFI variable LoaderDevicePartUUID: %m");
408 SecureBootMode secure
= efi_get_secure_boot_mode();
409 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
410 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type
), strna(fw_info
), ansi_normal());
411 printf(" Firmware Arch: %s\n", get_efi_arch());
412 printf(" Secure Boot: %s%s%s",
413 IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
) ? ansi_highlight_green() : ansi_normal(),
414 enabled_disabled(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)),
417 if (secure
!= SECURE_BOOT_DISABLED
)
418 printf(" (%s)\n", secure_boot_mode_to_string(secure
));
423 printf(" TPM2 Support: %s%s%s\n",
424 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? ansi_highlight_green() :
425 (s
& (TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
426 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? "yes" :
427 (s
& TPM2_SUPPORT_FIRMWARE
) ? "firmware only, driver unavailable" :
428 (s
& TPM2_SUPPORT_DRIVER
) ? "driver only, firmware unavailable" : "no",
431 k
= efi_measured_uki(LOG_DEBUG
);
433 printf(" Measured UKI: %syes%s\n", ansi_highlight_green(), ansi_normal());
435 printf(" Measured UKI: no\n");
438 printf(" Measured UKI: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
441 k
= efi_get_reboot_to_firmware();
443 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
445 printf(" Boot into FW: supported\n");
446 else if (k
== -EOPNOTSUPP
)
447 printf(" Boot into FW: not supported\n");
450 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
454 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
455 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader
), ansi_normal());
457 for (size_t i
= 0; i
< ELEMENTSOF(loader_flags
); i
++)
458 print_yes_no_line(i
== 0, FLAGS_SET(loader_features
, loader_flags
[i
].flag
), loader_flags
[i
].name
);
460 sd_id128_t bootloader_esp_uuid
;
461 bool have_bootloader_esp_uuid
= efi_loader_get_device_part_uuid(&bootloader_esp_uuid
) >= 0;
463 print_yes_no_line(false, have_bootloader_esp_uuid
, "Boot loader sets ESP information");
464 if (have_bootloader_esp_uuid
&& !sd_id128_is_null(esp_uuid
) &&
465 !sd_id128_equal(esp_uuid
, bootloader_esp_uuid
))
466 printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR
" vs. "SD_ID128_UUID_FORMAT_STR
")!\n",
467 SD_ID128_FORMAT_VAL(bootloader_esp_uuid
),
468 SD_ID128_FORMAT_VAL(esp_uuid
));
471 printf(" Stub: %s\n", stub
);
472 for (size_t i
= 0; i
< ELEMENTSOF(stub_flags
); i
++)
473 print_yes_no_line(i
== 0, FLAGS_SET(stub_features
, stub_flags
[i
].flag
), stub_flags
[i
].name
);
475 if (!sd_id128_is_null(loader_part_uuid
))
476 printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
477 SD_ID128_FORMAT_VAL(loader_part_uuid
));
479 printf(" ESP: n/a\n");
480 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), strna(loader_path
));
483 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
484 have
= access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken
)), F_OK
) >= 0;
485 printf(" System Token: %s\n", have
? "set" : "not set");
488 _cleanup_free_
char *p
= NULL
;
490 p
= path_join(arg_esp_path
, "/loader/random-seed");
494 have
= access(p
, F_OK
) >= 0;
495 printf(" Exists: %s\n", yes_no(have
));
500 printf("%sSystem:%s\n"
501 "Not booted with EFI\n\n",
502 ansi_underline(), ansi_normal());
505 RET_GATHER(r
, status_binaries(arg_esp_path
, esp_uuid
));
507 if (!arg_root
&& is_efi_boot())
508 RET_GATHER(r
, status_variables());
510 if (arg_esp_path
|| arg_xbootldr_path
) {
511 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
513 k
= boot_config_load_and_select(&config
,
514 arg_esp_path
, esp_devid
,
515 arg_xbootldr_path
, xbootldr_devid
);
520 status_entries(&config
,
521 arg_esp_path
, esp_uuid
,
522 arg_xbootldr_path
, xbootldr_uuid
));
528 static int ref_file(Hashmap
*known_files
, const char *fn
, int increment
) {
534 /* just gracefully ignore this. This way the caller doesn't
535 have to verify whether the bootloader entry is relevant */
539 n
= PTR_TO_INT(hashmap_get2(known_files
, fn
, (void**)&k
));
545 (void) hashmap_remove(known_files
, fn
);
548 _cleanup_free_
char *t
= NULL
;
553 r
= hashmap_put(known_files
, t
, INT_TO_PTR(n
));
558 r
= hashmap_update(known_files
, fn
, INT_TO_PTR(n
));
566 static void deref_unlink_file(Hashmap
*known_files
, const char *fn
, const char *root
) {
567 _cleanup_free_
char *path
= NULL
;
572 /* just gracefully ignore this. This way the caller doesn't
573 have to verify whether the bootloader entry is relevant */
577 r
= ref_file(known_files
, fn
, -1);
579 return (void) log_warning_errno(r
, "Failed to deref \"%s\", ignoring: %m", fn
);
584 r
= chase_and_access(fn
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, F_OK
, &path
);
586 log_info_errno(r
, "Unable to determine whether \"%s\" exists, ignoring: %m", fn
);
588 log_info("Would remove \"%s\"", path
);
592 r
= chase_and_unlink(fn
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, 0, &path
);
594 log_info("Removed \"%s\"", path
);
595 else if (r
!= -ENOENT
)
596 return (void) log_warning_errno(r
, "Failed to remove \"%s\", ignoring: %m", fn
);
598 _cleanup_free_
char *d
= NULL
;
599 if (path_extract_directory(fn
, &d
) >= 0 && !path_equal(d
, "/")) {
600 r
= chase_and_unlink(d
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, AT_REMOVEDIR
, NULL
);
601 if (r
< 0 && !IN_SET(r
, -ENOTEMPTY
, -ENOENT
))
602 log_warning_errno(r
, "Failed to remove directory \"%s\", ignoring: %m", d
);
606 static int count_known_files(const BootConfig
*config
, const char* root
, Hashmap
**ret_known_files
) {
607 _cleanup_(hashmap_free_free_keyp
) Hashmap
*known_files
= NULL
;
611 assert(ret_known_files
);
613 known_files
= hashmap_new(&path_hash_ops
);
617 for (size_t i
= 0; i
< config
->n_entries
; i
++) {
618 const BootEntry
*e
= config
->entries
+ i
;
620 if (!path_equal(e
->root
, root
))
623 r
= ref_file(known_files
, e
->kernel
, +1);
626 r
= ref_file(known_files
, e
->efi
, +1);
629 STRV_FOREACH(s
, e
->initrd
) {
630 r
= ref_file(known_files
, *s
, +1);
634 r
= ref_file(known_files
, e
->device_tree
, +1);
637 STRV_FOREACH(s
, e
->device_tree_overlay
) {
638 r
= ref_file(known_files
, *s
, +1);
644 *ret_known_files
= TAKE_PTR(known_files
);
649 static int boot_config_find_in(const BootConfig
*config
, const char *root
, const char *id
) {
655 for (size_t i
= 0; i
< config
->n_entries
; i
++)
656 if (path_equal(config
->entries
[i
].root
, root
) &&
657 fnmatch(id
, config
->entries
[i
].id
, FNM_CASEFOLD
) == 0)
663 static int unlink_entry(const BootConfig
*config
, const char *root
, const char *id
) {
664 _cleanup_(hashmap_free_free_keyp
) Hashmap
*known_files
= NULL
;
665 const BootEntry
*e
= NULL
;
670 r
= count_known_files(config
, root
, &known_files
);
672 return log_error_errno(r
, "Failed to count files in %s: %m", root
);
674 r
= boot_config_find_in(config
, root
, id
);
678 if (r
== config
->default_entry
)
679 log_warning("%s is the default boot entry", id
);
680 if (r
== config
->selected_entry
)
681 log_warning("%s is the selected boot entry", id
);
683 e
= &config
->entries
[r
];
685 deref_unlink_file(known_files
, e
->kernel
, e
->root
);
686 deref_unlink_file(known_files
, e
->efi
, e
->root
);
687 STRV_FOREACH(s
, e
->initrd
)
688 deref_unlink_file(known_files
, *s
, e
->root
);
689 deref_unlink_file(known_files
, e
->device_tree
, e
->root
);
690 STRV_FOREACH(s
, e
->device_tree_overlay
)
691 deref_unlink_file(known_files
, *s
, e
->root
);
694 log_info("Would remove \"%s\"", e
->path
);
696 r
= chase_and_unlink(e
->path
, root
, CHASE_PROHIBIT_SYMLINKS
, 0, NULL
);
698 return log_error_errno(r
, "Failed to remove \"%s\": %m", e
->path
);
700 log_info("Removed %s", e
->path
);
706 static int list_remove_orphaned_file(
707 RecurseDirEvent event
,
711 const struct dirent
*de
,
712 const struct statx
*sx
,
715 Hashmap
*known_files
= userdata
;
720 if (event
!= RECURSE_DIR_ENTRY
)
721 return RECURSE_DIR_CONTINUE
;
723 if (hashmap_get(known_files
, path
))
724 return RECURSE_DIR_CONTINUE
; /* keep! */
727 log_info("Would remove %s", path
);
728 else if (unlinkat(dir_fd
, de
->d_name
, 0) < 0)
729 log_warning_errno(errno
, "Failed to remove \"%s\", ignoring: %m", path
);
731 log_info("Removed %s", path
);
733 return RECURSE_DIR_CONTINUE
;
736 static int cleanup_orphaned_files(
737 const BootConfig
*config
,
740 _cleanup_(hashmap_free_free_keyp
) Hashmap
*known_files
= NULL
;
741 _cleanup_free_
char *full
= NULL
, *p
= NULL
;
742 _cleanup_close_
int dir_fd
= -EBADF
;
748 log_info("Cleaning %s", root
);
750 r
= settle_entry_token();
754 r
= count_known_files(config
, root
, &known_files
);
756 return log_error_errno(r
, "Failed to count files in %s: %m", root
);
758 dir_fd
= chase_and_open(arg_entry_token
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
,
759 O_DIRECTORY
|O_CLOEXEC
, &full
);
760 if (dir_fd
== -ENOENT
)
763 return log_error_errno(dir_fd
, "Failed to open '%s/%s': %m", root
, arg_entry_token
);
765 p
= path_join("/", arg_entry_token
);
769 r
= recurse_dir(dir_fd
, p
, 0, UINT_MAX
, RECURSE_DIR_SORT
, list_remove_orphaned_file
, known_files
);
771 return log_error_errno(r
, "Failed to cleanup %s: %m", full
);
776 int verb_list(int argc
, char *argv
[], void *userdata
) {
777 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
778 dev_t esp_devid
= 0, xbootldr_devid
= 0;
781 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
782 * things: turn off logging about access errors and turn off potentially privileged device probing.
783 * Here we're interested in the latter but not the former, hence request the mode, and log about
786 r
= acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL
, NULL
, NULL
, NULL
, &esp_devid
);
787 if (r
== -EACCES
) /* We really need the ESP path for this call, hence also log about access errors */
788 return log_error_errno(r
, "Failed to determine ESP location: %m");
792 r
= acquire_xbootldr(/* unprivileged_mode= */ -1, NULL
, &xbootldr_devid
);
794 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
798 r
= boot_config_load_and_select(&config
, arg_esp_path
, esp_devid
, arg_xbootldr_path
, xbootldr_devid
);
802 if (config
.n_entries
== 0 && FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
803 log_info("No boot loader entries found.");
807 if (streq(argv
[0], "list")) {
808 pager_open(arg_pager_flags
);
809 return show_boot_entries(&config
, arg_json_format_flags
);
810 } else if (streq(argv
[0], "cleanup")) {
811 if (arg_xbootldr_path
&& xbootldr_devid
!= esp_devid
)
812 cleanup_orphaned_files(&config
, arg_xbootldr_path
);
813 return cleanup_orphaned_files(&config
, arg_esp_path
);
815 assert(streq(argv
[0], "unlink"));
816 if (arg_xbootldr_path
&& xbootldr_devid
!= esp_devid
) {
817 r
= unlink_entry(&config
, arg_xbootldr_path
, argv
[1]);
818 if (r
== 0 || r
!= -ENOENT
)
821 return unlink_entry(&config
, arg_esp_path
, argv
[1]);
825 int verb_unlink(int argc
, char *argv
[], void *userdata
) {
826 return verb_list(argc
, argv
, userdata
);