]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/bootctl-status.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / boot / bootctl-status.c
CommitLineData
da36788f
LP
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-symlinks.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 "terminal-util.h"
22#include "tpm2-util.h"
23
24static int boot_config_load_and_select(
25 BootConfig *config,
26 const char *esp_path,
27 dev_t esp_devid,
28 const char *xbootldr_path,
29 dev_t xbootldr_devid) {
30
31 int r;
32
33 /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
34 * find the same entries twice. */
35 bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
36
37 r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
38 if (r < 0)
39 return r;
40
41 if (!arg_root) {
42 _cleanup_strv_free_ char **efi_entries = NULL;
43
44 r = efi_loader_get_entries(&efi_entries);
45 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
46 log_debug_errno(r, "Boot loader reported no entries.");
47 else if (r < 0)
48 log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
49 else
50 (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
51 }
52
53 return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
54}
55
56static int status_entries(
57 const BootConfig *config,
58 const char *esp_path,
59 sd_id128_t esp_partition_uuid,
60 const char *xbootldr_path,
61 sd_id128_t xbootldr_partition_uuid) {
62
63 sd_id128_t dollar_boot_partition_uuid;
64 const char *dollar_boot_path;
65 int r;
66
67 assert(config);
68 assert(esp_path || xbootldr_path);
69
70 if (xbootldr_path) {
71 dollar_boot_path = xbootldr_path;
72 dollar_boot_partition_uuid = xbootldr_partition_uuid;
73 } else {
74 dollar_boot_path = esp_path;
75 dollar_boot_partition_uuid = esp_partition_uuid;
76 }
77
78 printf("%sBoot Loader Entries:%s\n"
79 " $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
80 if (!sd_id128_is_null(dollar_boot_partition_uuid))
81 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
82 SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
83 printf("\n\n");
84
85 if (config->default_entry < 0)
86 printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
87 else {
88 printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
89
90 r = show_boot_entry(
91 boot_config_default_entry(config),
92 /* show_as_default= */ false,
93 /* show_as_selected= */ false,
94 /* show_discovered= */ false);
95 if (r > 0)
96 /* < 0 is already logged by the function itself, let's just emit an extra warning if
97 the default entry is broken */
98 printf("\nWARNING: default boot entry is broken\n");
99 }
100
101 return 0;
102}
103
104static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
105 _cleanup_free_ char *title = NULL;
106 _cleanup_free_ char *path = NULL;
107 sd_id128_t partition;
108 bool active;
109 int r;
110
111 assert(n_printed);
112
113 r = efi_get_boot_option(id, &title, &partition, &path, &active);
114 if (r == -ENOENT) {
115 log_debug_errno(r, "Boot option 0x%04X referenced but missing, ignoring: %m", id);
116 return 0;
117 }
118 if (r < 0)
119 return log_error_errno(r, "Failed to read boot option 0x%04X: %m", id);
120
121 /* print only configured entries with partition information */
122 if (!path || sd_id128_is_null(partition)) {
123 log_debug("Ignoring boot entry 0x%04X without partition information.", id);
124 return 0;
125 }
126
127 efi_tilt_backslashes(path);
128
129 if (*n_printed == 0) /* Print section title before first entry */
130 printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
131
132 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
133 printf(" ID: 0x%04X\n", id);
134 printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
135 printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
136 SD_ID128_FORMAT_VAL(partition));
137 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
138 printf("\n");
139
140 (*n_printed)++;
141 return 1;
142}
143
144static int status_variables(void) {
145 _cleanup_free_ uint16_t *options = NULL, *order = NULL;
146 int n_options, n_order, n_printed = 0;
147
148 n_options = efi_get_boot_options(&options);
149 if (n_options == -ENOENT)
150 return log_error_errno(n_options,
151 "Failed to access EFI variables, efivarfs"
152 " needs to be available at /sys/firmware/efi/efivars/.");
153 if (n_options < 0)
154 return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
155
156 n_order = efi_get_boot_order(&order);
157 if (n_order == -ENOENT)
158 n_order = 0;
159 else if (n_order < 0)
160 return log_error_errno(n_order, "Failed to read EFI boot order: %m");
161
162 /* print entries in BootOrder first */
163 for (int i = 0; i < n_order; i++)
164 (void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
165
166 /* print remaining entries */
167 for (int i = 0; i < n_options; i++) {
168 for (int j = 0; j < n_order; j++)
169 if (options[i] == order[j])
170 goto next_option;
171
172 (void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
173
174 next_option:
175 continue;
176 }
177
178 if (n_printed == 0)
179 printf("No boot loaders listed in EFI Variables.\n\n");
180
181 return 0;
182}
183
184static int enumerate_binaries(
185 const char *esp_path,
186 const char *path,
187 const char *prefix,
188 char **previous,
189 bool *is_first) {
190
191 _cleanup_closedir_ DIR *d = NULL;
192 _cleanup_free_ char *p = NULL;
193 int c = 0, r;
194
195 assert(esp_path);
196 assert(path);
197 assert(previous);
198 assert(is_first);
199
200 r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
201 if (r == -ENOENT)
202 return 0;
203 if (r < 0)
204 return log_error_errno(r, "Failed to read \"%s/%s\": %m", esp_path, path);
205
206 FOREACH_DIRENT(de, d, break) {
207 _cleanup_free_ char *v = NULL;
254d1313 208 _cleanup_close_ int fd = -EBADF;
da36788f
LP
209
210 if (!endswith_no_case(de->d_name, ".efi"))
211 continue;
212
213 if (prefix && !startswith_no_case(de->d_name, prefix))
214 continue;
215
216 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
217 if (fd < 0)
218 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
219
220 r = get_file_version(fd, &v);
221 if (r < 0)
222 return r;
223
224 if (*previous) { /* let's output the previous entry now, since now we know that there will be one more, and can draw the tree glyph properly */
225 printf(" %s %s%s\n",
226 *is_first ? "File:" : " ",
227 special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
228 *is_first = false;
229 *previous = mfree(*previous);
230 }
231
232 /* Do not output this entry immediately, but store what should be printed in a state
233 * variable, because we only will know the tree glyph to print (branch or final edge) once we
234 * read one more entry */
235 if (r > 0)
236 r = asprintf(previous, "/%s/%s (%s%s%s)", path, de->d_name, ansi_highlight(), v, ansi_normal());
237 else
238 r = asprintf(previous, "/%s/%s", path, de->d_name);
239 if (r < 0)
240 return log_oom();
241
242 c++;
243 }
244
245 return c;
246}
247
248static int status_binaries(const char *esp_path, sd_id128_t partition) {
249 _cleanup_free_ char *last = NULL;
250 bool is_first = true;
251 int r, k;
252
253 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
254
255 if (!esp_path) {
256 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
257 return -ENOENT;
258 }
259
260 printf(" ESP: %s", esp_path);
261 if (!sd_id128_is_null(partition))
262 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
263 printf("\n");
264
265 r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
266 if (r < 0) {
267 printf("\n");
268 return r;
269 }
270
271 k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
272 if (k < 0) {
273 printf("\n");
274 return k;
275 }
276
277 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 */
278 printf(" %s %s%s\n",
279 is_first ? "File:" : " ",
280 special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
281
282 if (r == 0 && !arg_quiet)
283 log_info("systemd-boot not installed in ESP.");
284 if (k == 0 && !arg_quiet)
285 log_info("No default/fallback boot loader installed in ESP.");
286
287 printf("\n");
288 return 0;
289}
290
291static void read_efi_var(const char *variable, char **ret) {
292 int r;
293
294 r = efi_get_variable_string(variable, ret);
295 if (r < 0 && r != -ENOENT)
296 log_warning_errno(r, "Failed to read EFI variable %s: %m", variable);
297}
298
299static void print_yes_no_line(bool first, bool good, const char *name) {
300 printf("%s%s %s\n",
301 first ? " Features: " : " ",
302 COLOR_MARK_BOOL(good),
303 name);
304}
305
306int verb_status(int argc, char *argv[], void *userdata) {
307 sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
308 dev_t esp_devid = 0, xbootldr_devid = 0;
309 int r, k;
310
311 r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid, &esp_devid);
312 if (arg_print_esp_path) {
313 if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
314 * error the find_esp_and_warn() won't log on its own) */
315 return log_error_errno(r, "Failed to determine ESP location: %m");
316 if (r < 0)
317 return r;
318
319 puts(arg_esp_path);
320 }
321
322 r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid, &xbootldr_devid);
323 if (arg_print_dollar_boot_path) {
324 if (r == -EACCES)
325 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
326 if (r < 0)
327 return r;
328
329 const char *path = arg_dollar_boot_path();
330 if (!path)
331 return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Failed to determine XBOOTLDR location: %m");
332
333 puts(path);
334 }
335
336 if (arg_print_esp_path || arg_print_dollar_boot_path)
337 return 0;
338
339 r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
340 * show what we can show */
341
342 pager_open(arg_pager_flags);
343
344 if (!arg_root && is_efi_boot()) {
345 static const struct {
346 uint64_t flag;
347 const char *name;
348 } loader_flags[] = {
349 { EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
350 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
351 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
352 { EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
353 { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
354 { EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
355 { EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
356 { EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
357 { EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
358 { EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
359 { EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
360 };
361 static const struct {
362 uint64_t flag;
363 const char *name;
364 } stub_flags[] = {
365 { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
366 { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
367 { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
368 { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
369 { EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
370 };
371 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
372 sd_id128_t loader_part_uuid = SD_ID128_NULL;
373 uint64_t loader_features = 0, stub_features = 0;
374 Tpm2Support s;
375 int have;
376
377 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
378 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
379 read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
380 read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
381 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
382 (void) efi_loader_get_features(&loader_features);
383 (void) efi_stub_get_features(&stub_features);
384
385 if (loader_path)
386 efi_tilt_backslashes(loader_path);
387
388 k = efi_loader_get_device_part_uuid(&loader_part_uuid);
389 if (k < 0 && k != -ENOENT)
390 r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
391
392 SecureBootMode secure = efi_get_secure_boot_mode();
393 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
394 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
395 printf(" Firmware Arch: %s\n", get_efi_arch());
396 printf(" Secure Boot: %sd (%s)\n",
397 enable_disable(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
398 secure_boot_mode_to_string(secure));
399
400 s = tpm2_support();
401 printf(" TPM2 Support: %s%s%s\n",
402 FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
403 (s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
404 FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? "yes" :
405 (s & TPM2_SUPPORT_FIRMWARE) ? "firmware only, driver unavailable" :
406 (s & TPM2_SUPPORT_DRIVER) ? "driver only, firmware unavailable" : "no",
407 ansi_normal());
408
409 k = efi_get_reboot_to_firmware();
410 if (k > 0)
411 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
412 else if (k == 0)
413 printf(" Boot into FW: supported\n");
414 else if (k == -EOPNOTSUPP)
415 printf(" Boot into FW: not supported\n");
416 else {
417 errno = -k;
418 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
419 }
420 printf("\n");
421
422 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
423 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
424
425 for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
426 print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
427
428 sd_id128_t bootloader_esp_uuid;
429 bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
430
431 print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information");
432 if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) &&
433 !sd_id128_equal(esp_uuid, bootloader_esp_uuid))
434 printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
435 SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
436 SD_ID128_FORMAT_VAL(esp_uuid));
437
438 if (stub) {
439 printf(" Stub: %s\n", stub);
440 for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
441 print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
442 }
443 if (!sd_id128_is_null(loader_part_uuid))
444 printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
445 SD_ID128_FORMAT_VAL(loader_part_uuid));
446 else
447 printf(" ESP: n/a\n");
448 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
449 printf("\n");
450
451 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
452 have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
453 printf(" System Token: %s\n", have ? "set" : "not set");
454
455 if (arg_esp_path) {
456 _cleanup_free_ char *p = NULL;
457
458 p = path_join(arg_esp_path, "/loader/random-seed");
459 if (!p)
460 return log_oom();
461
462 have = access(p, F_OK) >= 0;
463 printf(" Exists: %s\n", yes_no(have));
464 }
465
466 printf("\n");
467 } else
468 printf("%sSystem:%s\n"
469 "Not booted with EFI\n\n",
470 ansi_underline(), ansi_normal());
471
472 if (arg_esp_path) {
473 k = status_binaries(arg_esp_path, esp_uuid);
474 if (k < 0)
475 r = k;
476 }
477
478 if (!arg_root && is_efi_boot()) {
479 k = status_variables();
480 if (k < 0)
481 r = k;
482 }
483
484 if (arg_esp_path || arg_xbootldr_path) {
485 _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
486
487 k = boot_config_load_and_select(&config,
488 arg_esp_path, esp_devid,
489 arg_xbootldr_path, xbootldr_devid);
490 if (k < 0)
491 r = k;
492 else {
493 k = status_entries(&config,
494 arg_esp_path, esp_uuid,
495 arg_xbootldr_path, xbootldr_uuid);
496 if (k < 0)
497 r = k;
498 }
499 }
500
501 return r;
502}
503
504int verb_list(int argc, char *argv[], void *userdata) {
505 _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
506 dev_t esp_devid = 0, xbootldr_devid = 0;
507 int r;
508
509 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
510 * things: turn off logging about access errors and turn off potentially privileged device probing.
511 * Here we're interested in the latter but not the former, hence request the mode, and log about
512 * EACCES. */
513
514 r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid);
515 if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
516 return log_error_errno(r, "Failed to determine ESP location: %m");
517 if (r < 0)
518 return r;
519
520 r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL, &xbootldr_devid);
521 if (r == -EACCES)
522 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
523 if (r < 0)
524 return r;
525
526 r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
527 if (r < 0)
528 return r;
529
530 if (config.n_entries == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
531 log_info("No boot loader entries found.");
532 return 0;
533 }
534
535 pager_open(arg_pager_flags);
536 return show_boot_entries(&config, arg_json_format_flags);
537}