]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/bootctl-status.c
Merge pull request #31173 from yuwata/network-route-check-conflict
[thirdparty/systemd.git] / src / boot / bootctl-status.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/mman.h>
4 #include <unistd.h>
5
6 #include "bootctl.h"
7 #include "bootctl-status.h"
8 #include "bootctl-util.h"
9 #include "bootspec.h"
10 #include "chase.h"
11 #include "devnum-util.h"
12 #include "dirent-util.h"
13 #include "efi-api.h"
14 #include "efi-loader.h"
15 #include "errno-util.h"
16 #include "fd-util.h"
17 #include "fileio.h"
18 #include "find-esp.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"
24
25 static int boot_config_load_and_select(
26 BootConfig *config,
27 const char *esp_path,
28 dev_t esp_devid,
29 const char *xbootldr_path,
30 dev_t xbootldr_devid) {
31
32 int r;
33
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);
37
38 r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
39 if (r < 0)
40 return r;
41
42 if (!arg_root) {
43 _cleanup_strv_free_ char **efi_entries = NULL;
44
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.");
48 else if (r < 0)
49 log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
50 else
51 (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
52 }
53
54 return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
55 }
56
57 static int status_entries(
58 const BootConfig *config,
59 const char *esp_path,
60 sd_id128_t esp_partition_uuid,
61 const char *xbootldr_path,
62 sd_id128_t xbootldr_partition_uuid) {
63
64 sd_id128_t dollar_boot_partition_uuid;
65 const char *dollar_boot_path;
66 int r;
67
68 assert(config);
69 assert(esp_path || xbootldr_path);
70
71 if (xbootldr_path) {
72 dollar_boot_path = xbootldr_path;
73 dollar_boot_partition_uuid = xbootldr_partition_uuid;
74 } else {
75 dollar_boot_path = esp_path;
76 dollar_boot_partition_uuid = esp_partition_uuid;
77 }
78
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);
86 printf("\n\n");
87
88 if (config->default_entry < 0)
89 printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
90 else {
91 printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
92
93 r = show_boot_entry(
94 boot_config_default_entry(config),
95 &config->global_addons,
96 /* show_as_default= */ false,
97 /* show_as_selected= */ false,
98 /* show_discovered= */ false);
99 if (r > 0)
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");
103 }
104
105 return 0;
106 }
107
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;
112 bool active;
113 int r;
114
115 assert(n_printed);
116
117 r = efi_get_boot_option(id, &title, &partition, &path, &active);
118 if (r == -ENOENT) {
119 log_debug_errno(r, "Boot option 0x%04X referenced but missing, ignoring: %m", id);
120 return 0;
121 }
122 if (r < 0)
123 return log_error_errno(r, "Failed to read boot option 0x%04X: %m", id);
124
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);
128 return 0;
129 }
130
131 efi_tilt_backslashes(path);
132
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());
135
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);
142 printf("\n");
143
144 (*n_printed)++;
145 return 1;
146 }
147
148 static int status_variables(void) {
149 _cleanup_free_ uint16_t *options = NULL, *order = NULL;
150 int n_options, n_order, n_printed = 0;
151
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/.");
157 if (n_options < 0)
158 return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
159
160 n_order = efi_get_boot_order(&order);
161 if (n_order == -ENOENT)
162 n_order = 0;
163 else if (n_order < 0)
164 return log_error_errno(n_order, "Failed to read EFI boot order: %m");
165
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);
169
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])
174 goto next_option;
175
176 (void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
177
178 next_option:
179 continue;
180 }
181
182 if (n_printed == 0)
183 printf("No boot loaders listed in EFI Variables.\n\n");
184
185 return 0;
186 }
187
188 static int enumerate_binaries(
189 const char *esp_path,
190 const char *path,
191 char **previous,
192 bool *is_first) {
193
194 _cleanup_closedir_ DIR *d = NULL;
195 _cleanup_free_ char *p = NULL;
196 int c = 0, r;
197
198 assert(esp_path);
199 assert(path);
200 assert(previous);
201 assert(is_first);
202
203 r = chase_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
204 if (r == -ENOENT)
205 return 0;
206 if (r < 0)
207 return log_error_errno(r, "Failed to read \"%s/%s\": %m", esp_path, path);
208
209 FOREACH_DIRENT(de, d, break) {
210 _cleanup_free_ char *v = NULL, *filename = NULL;
211 _cleanup_close_ int fd = -EBADF;
212
213 if (!endswith_no_case(de->d_name, ".efi"))
214 continue;
215
216 filename = path_join(p, de->d_name);
217 if (!filename)
218 return log_oom();
219 LOG_SET_PREFIX(filename);
220
221 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
222 if (fd < 0)
223 return log_error_errno(errno, "Failed to open file for reading: %m");
224
225 r = get_file_version(fd, &v);
226
227 if (r < 0 && r != -ESRCH)
228 return r;
229
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. */
232 printf(" %s %s%s\n",
233 *is_first ? "File:" : " ",
234 special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
235 *is_first = false;
236 *previous = mfree(*previous);
237 }
238
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());
246 if (r < 0)
247 return log_oom();
248
249 c++;
250 }
251
252 return c;
253 }
254
255 static int status_binaries(const char *esp_path, sd_id128_t partition) {
256 _cleanup_free_ char *last = NULL;
257 bool is_first = true;
258 int r, k;
259
260 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
261
262 if (!esp_path) {
263 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
264 return -ENOENT;
265 }
266
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));
270 printf("\n");
271
272 r = enumerate_binaries(esp_path, "EFI/systemd", &last, &is_first);
273 if (r < 0)
274 goto fail;
275
276 k = enumerate_binaries(esp_path, "EFI/BOOT", &last, &is_first);
277 if (k < 0) {
278 r = k;
279 goto fail;
280 }
281
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 */
283 printf(" %s %s%s\n",
284 is_first ? "File:" : " ",
285 special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
286
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.");
291
292 printf("\n");
293 return 0;
294
295 fail:
296 errno = -r;
297 printf(" File: (can't access %s: %m)\n\n", esp_path);
298 return r;
299 }
300
301 static void read_efi_var(const char *variable, char **ret) {
302 int r;
303
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);
307 }
308
309 static void print_yes_no_line(bool first, bool good, const char *name) {
310 printf("%s%s %s\n",
311 first ? " Features: " : " ",
312 COLOR_MARK_BOOL(good),
313 name);
314 }
315
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;
319 int r, k;
320
321 r = acquire_esp(/* unprivileged_mode= */ -1,
322 /* graceful= */ false,
323 /* ret_part= */ NULL,
324 /* ret_pstart= */ NULL,
325 /* ret_psize= */ NULL,
326 &esp_uuid,
327 &esp_devid);
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");
332 if (r < 0)
333 return r;
334
335 puts(arg_esp_path);
336 return 0;
337 }
338
339 r = acquire_xbootldr(
340 /* unprivileged_mode= */ -1,
341 &xbootldr_uuid,
342 &xbootldr_devid);
343 if (arg_print_dollar_boot_path) {
344 if (r == -EACCES)
345 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
346 if (r < 0)
347 return r;
348
349 const char *path = arg_dollar_boot_path();
350 if (!path)
351 return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Failed to determine XBOOTLDR location: %m");
352
353 puts(path);
354 return 0;
355 }
356
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 */
359
360 pager_open(arg_pager_flags);
361
362 if (!arg_root && is_efi_boot()) {
363 static const struct {
364 uint64_t flag;
365 const char *name;
366 } loader_flags[] = {
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" },
381 };
382 static const struct {
383 uint64_t flag;
384 const char *name;
385 } stub_flags[] = {
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" },
395 };
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;
399 Tpm2Support s;
400 int have;
401
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);
409
410 if (loader_path)
411 efi_tilt_backslashes(loader_path);
412
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");
416
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)),
424 ansi_normal());
425
426 if (secure != SECURE_BOOT_DISABLED)
427 printf(" (%s)\n", secure_boot_mode_to_string(secure));
428 else
429 printf("\n");
430
431 s = tpm2_support();
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",
438 ansi_normal());
439
440 k = efi_measured_uki(LOG_DEBUG);
441 if (k > 0)
442 printf(" Measured UKI: %syes%s\n", ansi_highlight_green(), ansi_normal());
443 else if (k == 0)
444 printf(" Measured UKI: no\n");
445 else {
446 errno = -k;
447 printf(" Measured UKI: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
448 }
449
450 k = efi_get_reboot_to_firmware();
451 if (k > 0)
452 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
453 else if (k == 0)
454 printf(" Boot into FW: supported\n");
455 else if (k == -EOPNOTSUPP)
456 printf(" Boot into FW: not supported\n");
457 else {
458 errno = -k;
459 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
460 }
461 printf("\n");
462
463 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
464 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
465
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);
468
469 sd_id128_t bootloader_esp_uuid;
470 bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
471
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));
478
479 if (stub) {
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);
483 }
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));
487 else
488 printf(" ESP: n/a\n");
489 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
490 printf("\n");
491
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");
495
496 if (arg_esp_path) {
497 _cleanup_free_ char *p = NULL;
498
499 p = path_join(arg_esp_path, "/loader/random-seed");
500 if (!p)
501 return log_oom();
502
503 have = access(p, F_OK) >= 0;
504 printf(" Exists: %s\n", yes_no(have));
505 }
506
507 printf("\n");
508 } else
509 printf("%sSystem:%s\n"
510 "Not booted with EFI\n\n",
511 ansi_underline(), ansi_normal());
512
513 if (arg_esp_path)
514 RET_GATHER(r, status_binaries(arg_esp_path, esp_uuid));
515
516 if (!arg_root && is_efi_boot())
517 RET_GATHER(r, status_variables());
518
519 if (arg_esp_path || arg_xbootldr_path) {
520 _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
521
522 k = boot_config_load_and_select(&config,
523 arg_esp_path, esp_devid,
524 arg_xbootldr_path, xbootldr_devid);
525 RET_GATHER(r, k);
526
527 if (k >= 0)
528 RET_GATHER(r,
529 status_entries(&config,
530 arg_esp_path, esp_uuid,
531 arg_xbootldr_path, xbootldr_uuid));
532 }
533
534 return r;
535 }
536
537 static int ref_file(Hashmap *known_files, const char *fn, int increment) {
538 char *k = NULL;
539 int n, r;
540
541 assert(known_files);
542
543 /* just gracefully ignore this. This way the caller doesn't
544 have to verify whether the bootloader entry is relevant */
545 if (!fn)
546 return 0;
547
548 n = PTR_TO_INT(hashmap_get2(known_files, fn, (void**)&k));
549 n += increment;
550
551 assert(n >= 0);
552
553 if (n == 0) {
554 (void) hashmap_remove(known_files, fn);
555 free(k);
556 } else if (!k) {
557 _cleanup_free_ char *t = NULL;
558
559 t = strdup(fn);
560 if (!t)
561 return -ENOMEM;
562 r = hashmap_put(known_files, t, INT_TO_PTR(n));
563 if (r < 0)
564 return r;
565 TAKE_PTR(t);
566 } else {
567 r = hashmap_update(known_files, fn, INT_TO_PTR(n));
568 if (r < 0)
569 return r;
570 }
571
572 return n;
573 }
574
575 static void deref_unlink_file(Hashmap *known_files, const char *fn, const char *root) {
576 _cleanup_free_ char *path = NULL;
577 int r;
578
579 assert(known_files);
580
581 /* just gracefully ignore this. This way the caller doesn't
582 have to verify whether the bootloader entry is relevant */
583 if (!fn || !root)
584 return;
585
586 r = ref_file(known_files, fn, -1);
587 if (r < 0)
588 return (void) log_warning_errno(r, "Failed to deref \"%s\", ignoring: %m", fn);
589 if (r > 0)
590 return;
591
592 if (arg_dry_run) {
593 r = chase_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, &path);
594 if (r < 0)
595 log_info_errno(r, "Unable to determine whether \"%s\" exists, ignoring: %m", fn);
596 else
597 log_info("Would remove \"%s\"", path);
598 return;
599 }
600
601 r = chase_and_unlink(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, 0, &path);
602 if (r >= 0)
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);
606
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);
612 }
613 }
614
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;
617 int r;
618
619 assert(config);
620 assert(ret_known_files);
621
622 known_files = hashmap_new(&path_hash_ops);
623 if (!known_files)
624 return -ENOMEM;
625
626 for (size_t i = 0; i < config->n_entries; i++) {
627 const BootEntry *e = config->entries + i;
628
629 if (!path_equal(e->root, root))
630 continue;
631
632 r = ref_file(known_files, e->kernel, +1);
633 if (r < 0)
634 return r;
635 r = ref_file(known_files, e->efi, +1);
636 if (r < 0)
637 return r;
638 STRV_FOREACH(s, e->initrd) {
639 r = ref_file(known_files, *s, +1);
640 if (r < 0)
641 return r;
642 }
643 r = ref_file(known_files, e->device_tree, +1);
644 if (r < 0)
645 return r;
646 STRV_FOREACH(s, e->device_tree_overlay) {
647 r = ref_file(known_files, *s, +1);
648 if (r < 0)
649 return r;
650 }
651 }
652
653 *ret_known_files = TAKE_PTR(known_files);
654
655 return 0;
656 }
657
658 static int boot_config_find_in(const BootConfig *config, const char *root, const char *id) {
659 assert(config);
660
661 if (!root || !id)
662 return -ENOENT;
663
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)
667 return i;
668
669 return -ENOENT;
670 }
671
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;
675 int r;
676
677 assert(config);
678
679 r = count_known_files(config, root, &known_files);
680 if (r < 0)
681 return log_error_errno(r, "Failed to count files in %s: %m", root);
682
683 r = boot_config_find_in(config, root, id);
684 if (r < 0)
685 return r;
686
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);
691
692 e = &config->entries[r];
693
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);
701
702 if (arg_dry_run)
703 log_info("Would remove \"%s\"", e->path);
704 else {
705 r = chase_and_unlink(e->path, root, CHASE_PROHIBIT_SYMLINKS, 0, NULL);
706 if (r < 0)
707 return log_error_errno(r, "Failed to remove \"%s\": %m", e->path);
708
709 log_info("Removed %s", e->path);
710 }
711
712 return 0;
713 }
714
715 static int list_remove_orphaned_file(
716 RecurseDirEvent event,
717 const char *path,
718 int dir_fd,
719 int inode_fd,
720 const struct dirent *de,
721 const struct statx *sx,
722 void *userdata) {
723
724 Hashmap *known_files = userdata;
725
726 assert(path);
727 assert(known_files);
728
729 if (event != RECURSE_DIR_ENTRY)
730 return RECURSE_DIR_CONTINUE;
731
732 if (hashmap_get(known_files, path))
733 return RECURSE_DIR_CONTINUE; /* keep! */
734
735 if (arg_dry_run)
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);
739 else
740 log_info("Removed %s", path);
741
742 return RECURSE_DIR_CONTINUE;
743 }
744
745 static int cleanup_orphaned_files(
746 const BootConfig *config,
747 const char *root) {
748
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;
752 int r;
753
754 assert(config);
755 assert(root);
756
757 log_info("Cleaning %s", root);
758
759 r = settle_entry_token();
760 if (r < 0)
761 return r;
762
763 r = count_known_files(config, root, &known_files);
764 if (r < 0)
765 return log_error_errno(r, "Failed to count files in %s: %m", root);
766
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)
770 return 0;
771 if (dir_fd < 0)
772 return log_error_errno(dir_fd, "Failed to open '%s/%s': %m", root, arg_entry_token);
773
774 p = path_join("/", arg_entry_token);
775 if (!p)
776 return log_oom();
777
778 r = recurse_dir(dir_fd, p, 0, UINT_MAX, RECURSE_DIR_SORT, list_remove_orphaned_file, known_files);
779 if (r < 0)
780 return log_error_errno(r, "Failed to cleanup %s: %m", full);
781
782 return r;
783 }
784
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;
788 int r;
789
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
793 * EACCES. */
794
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");
798 if (r < 0)
799 return r;
800
801 r = acquire_xbootldr(/* unprivileged_mode= */ -1, NULL, &xbootldr_devid);
802 if (r == -EACCES)
803 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
804 if (r < 0)
805 return r;
806
807 r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
808 if (r < 0)
809 return r;
810
811 if (config.n_entries == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
812 log_info("No boot loader entries found.");
813 return 0;
814 }
815
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);
823 } else {
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)
828 return r;
829 }
830 return unlink_entry(&config, arg_esp_path, argv[1]);
831 }
832 }
833
834 int verb_unlink(int argc, char *argv[], void *userdata) {
835 return verb_list(argc, argv, userdata);
836 }
837
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;
841 int r;
842
843 assert(link);
844
845 if (json_variant_elements(parameters) > 0)
846 return varlink_error_invalid_parameter(link, parameters);
847
848 r = acquire_esp(/* unprivileged_mode= */ false,
849 /* graceful= */ false,
850 /* ret_part= */ NULL,
851 /* ret_pstart= */ NULL,
852 /* ret_psize= */ NULL,
853 /* ret_uuid=*/ NULL,
854 &esp_devid);
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");
857 if (r < 0)
858 return r;
859
860 r = acquire_xbootldr(
861 /* unprivileged_mode= */ false,
862 /* ret_uuid= */ NULL,
863 &xbootldr_devid);
864 if (r == -EACCES)
865 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
866 if (r < 0)
867 return r;
868
869 r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
870 if (r < 0)
871 return r;
872
873 _cleanup_(json_variant_unrefp) JsonVariant *previous = NULL;
874 for (size_t i = 0; i < config.n_entries; i++) {
875 if (previous) {
876 r = varlink_notifyb(link, JSON_BUILD_OBJECT(
877 JSON_BUILD_PAIR_VARIANT("entry", previous)));
878 if (r < 0)
879 return r;
880
881 previous = json_variant_unref(previous);
882 }
883
884 r = boot_entry_to_json(&config, i, &previous);
885 if (r < 0)
886 return r;
887 }
888
889 return varlink_replyb(link, JSON_BUILD_OBJECT(
890 JSON_BUILD_PAIR_CONDITION(previous, "entry", JSON_BUILD_VARIANT(previous))));
891 }