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

systemd / systemd / 13877892945

15 Mar 2025 08:56PM UTC coverage: 71.915% (+0.2%) from 71.757%
13877892945

push

github

web-flow
Fix bootctl status to not print strange glyphs in logs (#36745)

146 of 198 new or added lines in 57 files covered. (73.74%)

153 existing lines in 28 files now uncovered.

296065 of 411690 relevant lines covered (71.91%)

715276.25 hits per line

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

57.36
/src/bootctl/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(
28✔
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;
28✔
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);
28✔
37

38
        r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
28✔
39
        if (r < 0)
28✔
40
                return r;
41

42
        if (!arg_root) {
28✔
43
                _cleanup_strv_free_ char **efi_entries = NULL;
×
44

45
                r = efi_loader_get_entries(&efi_entries);
14✔
46
                if (r == -ENOENT || ERRNO_IS_NEG_NOT_SUPPORTED(r))
14✔
47
                        log_debug_errno(r, "Boot loader reported no entries.");
8✔
48
                else if (r < 0)
6✔
49
                        log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
14✔
50
                else
51
                        (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
6✔
52
        }
53

54
        return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
28✔
55
}
56

57
static int status_entries(
15✔
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;
15✔
65
        const char *dollar_boot_path;
15✔
66
        int r;
15✔
67

68
        assert(config);
15✔
69
        assert(esp_path || xbootldr_path);
15✔
70

71
        if (xbootldr_path) {
15✔
72
                dollar_boot_path = xbootldr_path;
6✔
73
                dollar_boot_partition_uuid = xbootldr_partition_uuid;
6✔
74
        } else {
75
                dollar_boot_path = esp_path;
9✔
76
                dollar_boot_partition_uuid = esp_partition_uuid;
9✔
77
        }
78

79
        printf("%sBoot Loader Entries:%s\n"
30✔
80
               "        $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
81
        if (!sd_id128_is_null(dollar_boot_partition_uuid))
18✔
82
                printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
12✔
83
                       SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
12✔
84
        if (settle_entry_token() >= 0)
15✔
85
                printf("\n        token: %s", arg_entry_token);
15✔
86
        printf("\n\n");
15✔
87

88
        if (config->default_entry < 0)
15✔
89
                printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
6✔
90
        else {
91
                printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
18✔
92

93
                r = show_boot_entry(
9✔
94
                                boot_config_default_entry(config),
95
                                /* show_as_default= */ false,
96
                                /* show_as_selected= */ false,
97
                                /* show_discovered= */ false);
98
                if (r > 0)
9✔
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");
×
102
        }
103

104
        return 0;
15✔
105
}
106

107
static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
18✔
108
        _cleanup_free_ char *title = NULL;
36✔
109
        _cleanup_free_ char *path = NULL;
18✔
110
        sd_id128_t partition;
18✔
111
        bool active;
18✔
112
        int r;
18✔
113

114
        assert(n_printed);
18✔
115

116
        r = efi_get_boot_option(id, &title, &partition, &path, &active);
18✔
117
        if (r == -ENOENT) {
18✔
118
                log_debug_errno(r, "Boot option 0x%04X referenced but missing, ignoring: %m", id);
×
119
                return 0;
×
120
        }
121
        if (r < 0)
18✔
122
                return log_error_errno(r, "Failed to read boot option 0x%04X: %m", id);
×
123

124
        /* print only configured entries with partition information */
125
        if (!path || sd_id128_is_null(partition)) {
18✔
126
                log_debug("Ignoring boot entry 0x%04X without partition information.", id);
18✔
127
                return 0;
18✔
128
        }
129

130
        efi_tilt_backslashes(path);
×
131

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());
×
134

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));
×
NEW
140
        printf("         File: %s%s\n", glyph(GLYPH_TREE_RIGHT), path);
×
141
        printf("\n");
×
142

143
        (*n_printed)++;
×
144
        return 1;
×
145
}
146

147
static int status_variables(void) {
6✔
148
        _cleanup_free_ uint16_t *options = NULL, *order = NULL;
6✔
149
        int n_options, n_order, n_printed = 0;
6✔
150

151
        n_options = efi_get_boot_options(&options);
6✔
152
        if (n_options == -ENOENT)
6✔
153
                return log_error_errno(n_options,
×
154
                                       "Failed to access EFI variables, efivarfs"
155
                                       " needs to be available at /sys/firmware/efi/efivars/.");
156
        if (n_options < 0)
6✔
157
                return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
×
158

159
        n_order = efi_get_boot_order(&order);
6✔
160
        if (n_order == -ENOENT)
6✔
161
                n_order = 0;
162
        else if (n_order < 0)
6✔
163
                return log_error_errno(n_order, "Failed to read EFI boot order: %m");
×
164

165
        /* print entries in BootOrder first */
166
        for (int i = 0; i < n_order; i++)
24✔
167
                (void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
18✔
168

169
        /* print remaining entries */
170
        for (int i = 0; i < n_options; i++) {
24✔
171
                for (int j = 0; j < n_order; j++)
36✔
172
                        if (options[i] == order[j])
36✔
173
                                goto next_option;
18✔
174

175
                (void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
×
176

177
        next_option:
18✔
178
                continue;
18✔
179
        }
180

181
        if (n_printed == 0)
6✔
182
                printf("No boot loaders listed in EFI Variables.\n\n");
6✔
183

184
        return 0;
185
}
186

187
static int enumerate_binaries(
30✔
188
                const char *esp_path,
189
                const char *path,
190
                char **previous,
191
                bool *is_first) {
192

193
        _cleanup_closedir_ DIR *d = NULL;
30✔
194
        _cleanup_free_ char *p = NULL;
30✔
195
        int c = 0, r;
30✔
196

197
        assert(esp_path);
30✔
198
        assert(path);
30✔
199
        assert(previous);
30✔
200
        assert(is_first);
30✔
201

202
        r = chase_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
30✔
203
        if (r == -ENOENT)
30✔
204
                return 0;
205
        if (r < 0)
30✔
206
                return log_error_errno(r, "Failed to read \"%s/%s\": %m", esp_path, path);
×
207

208
        FOREACH_DIRENT(de, d, break) {
132✔
209
                _cleanup_free_ char *v = NULL, *filename = NULL;
42✔
210
                _cleanup_close_ int fd = -EBADF;
42✔
211

212
                if (!endswith_no_case(de->d_name, ".efi"))
42✔
213
                        continue;
×
214

215
                filename = path_join(p, de->d_name);
42✔
216
                if (!filename)
42✔
217
                        return log_oom();
×
218
                LOG_SET_PREFIX(filename);
84✔
219

220
                fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
42✔
221
                if (fd < 0)
42✔
222
                        return log_error_errno(errno, "Failed to open file for reading: %m");
×
223

224
                r = get_file_version(fd, &v);
42✔
225

226
                if (r < 0 && r != -ESRCH)
42✔
227
                        return r;
228

229
                if (*previous) { /* Let's output the previous entry now, since now we know that there will be
42✔
230
                                  * one more, and can draw the tree glyph properly. */
231
                        printf("         %s %s%s\n",
54✔
232
                               *is_first ? "File:" : "     ",
27✔
233
                               glyph(GLYPH_TREE_BRANCH), *previous);
234
                        *is_first = false;
27✔
235
                        *previous = mfree(*previous);
27✔
236
                }
237

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 */
42✔
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());
126✔
245
                if (r < 0)
42✔
246
                        return log_oom();
×
247

248
                c++;
42✔
249
        }
250

251
        return c;
252
}
253

254
static int status_binaries(const char *esp_path, sd_id128_t partition) {
15✔
255
        _cleanup_free_ char *last = NULL;
15✔
256
        bool is_first = true;
15✔
257
        int r, k;
15✔
258

259
        printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
30✔
260

261
        if (!esp_path) {
15✔
262
                printf("          ESP: Cannot find or access mount point of ESP.\n\n");
×
263
                return -ENOENT;
264
        }
265

266
        printf("          ESP: %s", esp_path);
15✔
267
        if (!sd_id128_is_null(partition))
18✔
268
                printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
12✔
269
        printf("\n");
15✔
270

271
        r = enumerate_binaries(esp_path, "EFI/systemd", &last, &is_first);
15✔
272
        if (r < 0)
15✔
273
                goto fail;
×
274

275
        k = enumerate_binaries(esp_path, "EFI/BOOT", &last, &is_first);
15✔
276
        if (k < 0) {
15✔
277
                r = k;
×
278
                goto fail;
×
279
        }
280

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 */
15✔
282
                printf("         %s %s%s\n",
30✔
283
                       is_first ? "File:" : "     ",
15✔
284
                       glyph(GLYPH_TREE_RIGHT), last);
285

286
        if (r == 0 && !arg_quiet)
15✔
287
                log_info("systemd-boot not installed in ESP.");
×
288
        if (k == 0 && !arg_quiet)
15✔
289
                log_info("No default/fallback boot loader installed in ESP.");
×
290

291
        printf("\n");
15✔
292
        return 0;
293

294
fail:
×
295
        errno = -r;
×
296
        printf("         File: (can't access %s: %m)\n\n", esp_path);
×
297
        return r;
298
}
299

300
static int efi_get_variable_string_and_warn(const char *variable, char **ret) {
54✔
301
        int r;
54✔
302

303
        r = efi_get_variable_string(variable, ret);
54✔
304
        if (r < 0 && r != -ENOENT)
54✔
305
                return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable);
×
306

307
        return r;
308
}
309

310
static int efi_get_variable_path_and_warn(const char *variable, char **ret) {
12✔
311
        int r;
12✔
312

313
        r = efi_get_variable_path(variable, ret);
12✔
314
        if (r < 0 && r != -ENOENT)
12✔
315
                return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable);
×
316

317
        return r;
318
}
319

320
static void print_yes_no_line(bool first, bool good, const char *name) {
204✔
321
        printf("%s%s %s\n",
600✔
322
               first ? "     Features: " : "               ",
323
               COLOR_MARK_BOOL(good),
204✔
324
               name);
325
}
204✔
326

327
int verb_status(int argc, char *argv[], void *userdata) {
33✔
328
        sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
33✔
329
        dev_t esp_devid = 0, xbootldr_devid = 0;
33✔
330
        int r, k;
33✔
331

332
        r = acquire_esp(/* unprivileged_mode= */ -1,
33✔
333
                        /* graceful= */ false,
334
                        /* ret_part= */ NULL,
335
                        /* ret_pstart= */ NULL,
336
                        /* ret_psize= */ NULL,
337
                        &esp_uuid,
338
                        &esp_devid);
339
        if (arg_print_esp_path) {
33✔
340
                if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
10✔
341
                                   * error the find_esp_and_warn() won't log on its own) */
342
                        return log_error_errno(r, "Failed to determine ESP location: %m");
33✔
343
                if (r < 0)
10✔
344
                        return r;
345

346
                puts(arg_esp_path);
8✔
347
                return 0;
8✔
348
        }
349

350
        r = acquire_xbootldr(
23✔
351
                        /* unprivileged_mode= */ -1,
352
                        &xbootldr_uuid,
353
                        &xbootldr_devid);
354
        if (arg_print_dollar_boot_path) {
23✔
355
                if (r == -EACCES)
8✔
356
                        return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
×
357
                if (r < 0)
8✔
358
                        return r;
359

360
                const char *path = arg_dollar_boot_path();
7✔
361
                if (!path)
3✔
362
                        return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Failed to determine XBOOTLDR location.");
1✔
363

364
                puts(path);
6✔
365
                return 0;
6✔
366
        }
367

368
        r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
15✔
369
                * show what we can show */
370

371
        pager_open(arg_pager_flags);
15✔
372

373
        if (arg_root)
15✔
374
                log_debug("Skipping 'System' section, operating offline.");
6✔
375
        else if (!is_efi_boot())
9✔
376
                printf("%sSystem:%s\n"
6✔
377
                       "Not booted with EFI\n\n",
378
                       ansi_underline(), ansi_normal());
379
        else {
380
                static const struct {
6✔
381
                        uint64_t flag;
382
                        const char *name;
383
                } loader_flags[] = {
384
                        { EFI_LOADER_FEATURE_BOOT_COUNTING,           "Boot counting"                         },
385
                        { EFI_LOADER_FEATURE_CONFIG_TIMEOUT,          "Menu timeout control"                  },
386
                        { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control"         },
387
                        { EFI_LOADER_FEATURE_ENTRY_DEFAULT,           "Default entry control"                 },
388
                        { EFI_LOADER_FEATURE_ENTRY_ONESHOT,           "One-shot entry control"                },
389
                        { EFI_LOADER_FEATURE_XBOOTLDR,                "Support for XBOOTLDR partition"        },
390
                        { EFI_LOADER_FEATURE_RANDOM_SEED,             "Support for passing random seed to OS" },
391
                        { EFI_LOADER_FEATURE_LOAD_DRIVER,             "Load drop-in drivers"                  },
392
                        { EFI_LOADER_FEATURE_SORT_KEY,                "Support Type #1 sort-key field"        },
393
                        { EFI_LOADER_FEATURE_SAVED_ENTRY,             "Support @saved pseudo-entry"           },
394
                        { EFI_LOADER_FEATURE_DEVICETREE,              "Support Type #1 devicetree field"      },
395
                        { EFI_LOADER_FEATURE_SECUREBOOT_ENROLL,       "Enroll SecureBoot keys"                },
396
                        { EFI_LOADER_FEATURE_RETAIN_SHIM,             "Retain SHIM protocols"                 },
397
                        { EFI_LOADER_FEATURE_MENU_DISABLE,            "Menu can be disabled"                  },
398
                        { EFI_LOADER_FEATURE_MULTI_PROFILE_UKI,       "Multi-Profile UKIs are supported"      },
399
                        { EFI_LOADER_FEATURE_REPORT_URL,              "Loader reports network boot URL"       },
400
                        { EFI_LOADER_FEATURE_TYPE1_UKI,               "Support Type #1 uki field"             },
401
                        { EFI_LOADER_FEATURE_TYPE1_UKI_URL,           "Support Type #1 uki-url field"         },
402
                };
403
                static const struct {
6✔
404
                        uint64_t flag;
405
                        const char *name;
406
                } stub_flags[] = {
407
                        { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION,     "Stub reports loader partition information"                   },
408
                        { EFI_STUB_FEATURE_REPORT_STUB_PARTITION,     "Stub reports stub partition information"                     },
409
                        { EFI_STUB_FEATURE_REPORT_URL,                "Stub reports network boot URL"                               },
410
                        { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS,       "Picks up credentials from boot partition"                    },
411
                        { EFI_STUB_FEATURE_PICK_UP_SYSEXTS,           "Picks up system extension images from boot partition"        },
412
                        { EFI_STUB_FEATURE_PICK_UP_CONFEXTS,          "Picks up configuration extension images from boot partition" },
413
                        { EFI_STUB_FEATURE_THREE_PCRS,                "Measures kernel+command line+sysexts"                        },
414
                        { EFI_STUB_FEATURE_RANDOM_SEED,               "Support for passing random seed to OS"                       },
415
                        { EFI_STUB_FEATURE_CMDLINE_ADDONS,            "Pick up .cmdline from addons"                                },
416
                        { EFI_STUB_FEATURE_CMDLINE_SMBIOS,            "Pick up .cmdline from SMBIOS Type 11"                        },
417
                        { EFI_STUB_FEATURE_DEVICETREE_ADDONS,         "Pick up .dtb from addons"                                    },
418
                        { EFI_STUB_FEATURE_MULTI_PROFILE_UKI,         "Stub understands profile selector"                           },
419
                };
420
                _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
×
421
                        *current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
6✔
422
                uint64_t loader_features = 0, stub_features = 0;
6✔
423
                int have;
6✔
424

425
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderFirmwareType"), &fw_type);
6✔
426
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderFirmwareInfo"), &fw_info);
6✔
427
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderInfo"), &loader);
6✔
428
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("StubInfo"), &stub);
6✔
429
                (void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &loader_path);
6✔
430
                (void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &stub_path);
6✔
431
                (void) efi_loader_get_features(&loader_features);
6✔
432
                (void) efi_stub_get_features(&stub_features);
6✔
433
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), &current_entry);
6✔
434
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &oneshot_entry);
6✔
435
                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &default_entry);
6✔
436

437
                SecureBootMode secure = efi_get_secure_boot_mode();
6✔
438
                printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
12✔
439
                printf("      Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
18✔
440
                printf(" Firmware Arch: %s\n", get_efi_arch());
6✔
441
                printf("   Secure Boot: %s%s%s",
12✔
442
                       IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED) ? ansi_highlight_green() : ansi_normal(),
6✔
443
                       enabled_disabled(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
6✔
444
                       ansi_normal());
445

446
                if (secure != SECURE_BOOT_DISABLED)
6✔
447
                        printf(" (%s)\n", secure_boot_mode_to_string(secure));
6✔
448
                else
449
                        printf("\n");
×
450

451
                Tpm2Support s = tpm2_support_full(TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER);
6✔
452
                printf("  TPM2 Support: %s%s%s\n",
18✔
453
                       FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
454
                       (s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
×
455
                       FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? "yes" :
6✔
456
                       (s & TPM2_SUPPORT_FIRMWARE) ? "firmware only, driver unavailable" :
×
457
                       (s & TPM2_SUPPORT_DRIVER) ? "driver only, firmware unavailable" : "no",
×
458
                       ansi_normal());
459

460
                k = efi_measured_uki(LOG_DEBUG);
6✔
461
                if (k > 0)
6✔
462
                        printf("  Measured UKI: %syes%s\n", ansi_highlight_green(), ansi_normal());
12✔
463
                else if (k == 0)
×
464
                        printf("  Measured UKI: no\n");
×
465
                else {
466
                        errno = -k;
×
467
                        printf("  Measured UKI: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
×
468
                }
469

470
                k = efi_get_reboot_to_firmware();
6✔
471
                if (k > 0)
6✔
472
                        printf("  Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
×
473
                else if (k == 0)
6✔
474
                        printf("  Boot into FW: supported\n");
6✔
475
                else if (k == -EOPNOTSUPP)
×
476
                        printf("  Boot into FW: not supported\n");
×
477
                else {
478
                        errno = -k;
×
479
                        printf("  Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
×
480
                }
481
                printf("\n");
6✔
482

483
                if (loader) {
6✔
484
                        printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
12✔
485
                        printf("      Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
18✔
486
                        for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
114✔
487
                                print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
108✔
488

489
                        sd_id128_t loader_partition_uuid = SD_ID128_NULL;
6✔
490
                        (void) efi_loader_get_device_part_uuid(&loader_partition_uuid);
6✔
491
                        print_yes_no_line(/* first= */ false, !sd_id128_is_null(loader_partition_uuid), "Boot loader set partition information");
12✔
492

493
                        _cleanup_free_ char *loader_url = NULL;
6✔
494
                        (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderDeviceURL"), &loader_url);
6✔
495
                        print_yes_no_line(/* first= */ false, !!loader_url, "Boot loader set network boot URL information");
6✔
496

497
                        if (!sd_id128_is_null(loader_partition_uuid)) {
6✔
498
                                if (!sd_id128_is_null(esp_uuid) && !sd_id128_equal(esp_uuid, loader_partition_uuid))
12✔
499
                                        printf("WARNING: The boot loader reports a different partition UUID than the detected ESP ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
×
500
                                               SD_ID128_FORMAT_VAL(loader_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid));
×
501

502
                                printf("    Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
6✔
503
                                       SD_ID128_FORMAT_VAL(loader_partition_uuid));
6✔
504
                        } else if (loader_path)
×
505
                                printf("    Partition: n/a\n");
×
506

507
                        if (loader_path)
6✔
508
                                printf("       Loader: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(loader_path));
6✔
509

510
                        if (loader_url)
6✔
511
                                printf(" Net Boot URL: %s\n", loader_url);
×
512

513
                        if (current_entry)
6✔
514
                                printf("Current Entry: %s\n", current_entry);
6✔
515
                        if (default_entry)
6✔
516
                                printf("Default Entry: %s\n", default_entry);
3✔
517
                        if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry))
6✔
518
                                printf("OneShot Entry: %s\n", oneshot_entry);
×
519

520
                        printf("\n");
6✔
521
                }
522

523
                if (stub) {
6✔
524
                        printf("%sCurrent Stub:%s\n", ansi_underline(), ansi_normal());
12✔
525
                        printf("      Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal());
18✔
526
                        for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
78✔
527
                                print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
72✔
528

529
                        sd_id128_t stub_partition_uuid = SD_ID128_NULL;
6✔
530
                        (void) efi_stub_get_device_part_uuid(&stub_partition_uuid);
6✔
531
                        print_yes_no_line(/* first= */ false, !sd_id128_is_null(stub_partition_uuid), "Stub loader set partition information");
12✔
532

533
                        _cleanup_free_ char *stub_url = NULL;
6✔
534
                        (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("StubDeviceURL"), &stub_url);
6✔
535
                        print_yes_no_line(/* first= */ false, !!stub_url, "Stub set network boot URL information");
6✔
536

537
                        if (!sd_id128_is_null(stub_partition_uuid)) {
6✔
538
                                if (!(!sd_id128_is_null(esp_uuid) && sd_id128_equal(esp_uuid, stub_partition_uuid)) &&
12✔
539
                                    !(!sd_id128_is_null(xbootldr_uuid) && sd_id128_equal(xbootldr_uuid, stub_partition_uuid)))
×
540
                                        printf("WARNING: The stub loader reports a different UUID than the detected ESP or XBOOTDLR partition ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR"/"SD_ID128_UUID_FORMAT_STR")!\n",
×
541
                                               SD_ID128_FORMAT_VAL(stub_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid), SD_ID128_FORMAT_VAL(xbootldr_uuid));
×
542

543
                                printf("    Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
6✔
544
                                       SD_ID128_FORMAT_VAL(stub_partition_uuid));
6✔
545
                        } else if (stub_path)
×
546
                                printf("    Partition: n/a\n");
×
547

548
                        if (stub_path)
6✔
549
                                printf("         Stub: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(stub_path));
6✔
550

551
                        if (stub_url)
6✔
552
                                printf(" Net Boot URL: %s\n", stub_url);
×
553

554
                        printf("\n");
6✔
555
                }
556

557
                printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
12✔
558
                have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderSystemToken")), F_OK) >= 0;
6✔
559
                printf(" System Token: %s\n", have ? "set" : "not set");
6✔
560

561
                if (arg_esp_path) {
6✔
562
                        _cleanup_free_ char *p = NULL;
6✔
563

564
                        p = path_join(arg_esp_path, "/loader/random-seed");
6✔
565
                        if (!p)
6✔
566
                                return log_oom();
×
567

568
                        r = access(p, F_OK);
6✔
569
                        if (r < 0 && errno != ENOENT)
6✔
570
                                printf("       Exists: Can't access %s (%m)\n", p);
×
571
                        else
572
                                printf("       Exists: %s\n", yes_no(r >= 0));
6✔
573
                }
574

575
                printf("\n");
6✔
576
        }
577

578
        if (arg_esp_path)
15✔
579
                RET_GATHER(r, status_binaries(arg_esp_path, esp_uuid));
15✔
580

581
        if (!arg_root && is_efi_boot())
15✔
582
                RET_GATHER(r, status_variables());
6✔
583

584
        if (arg_esp_path || arg_xbootldr_path) {
15✔
585
                _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
15✔
586

587
                k = boot_config_load_and_select(&config,
15✔
588
                                                arg_esp_path, esp_devid,
589
                                                arg_xbootldr_path, xbootldr_devid);
590
                RET_GATHER(r, k);
15✔
591

592
                if (k >= 0)
×
593
                        RET_GATHER(r,
15✔
594
                                   status_entries(&config,
595
                                                  arg_esp_path, esp_uuid,
596
                                                  arg_xbootldr_path, xbootldr_uuid));
597
        }
598

599
        return r;
600
}
601

602
static int ref_file(Hashmap **known_files, const char *fn, int increment) {
×
603
        char *k = NULL;
×
604
        int n, r;
×
605

606
        assert(known_files);
×
607

608
        /* just gracefully ignore this. This way the caller doesn't have to verify whether the bootloader
609
         * entry is relevant. */
610
        if (!fn)
×
611
                return 0;
×
612

613
        n = PTR_TO_INT(hashmap_get2(*known_files, fn, (void**)&k));
×
614
        n += increment;
×
615

616
        assert(n >= 0);
×
617

618
        if (n == 0) {
×
619
                (void) hashmap_remove(*known_files, fn);
×
620
                free(k);
×
621
        } else if (!k) {
×
622
                _cleanup_free_ char *t = NULL;
×
623

624
                t = strdup(fn);
×
625
                if (!t)
×
626
                        return -ENOMEM;
627
                r = hashmap_ensure_put(known_files, &path_hash_ops_free, t, INT_TO_PTR(n));
×
628
                if (r < 0)
×
629
                        return r;
630
                TAKE_PTR(t);
631
        } else {
632
                r = hashmap_update(*known_files, fn, INT_TO_PTR(n));
×
633
                if (r < 0)
×
634
                        return r;
×
635
        }
636

637
        return n;
638
}
639

640
static void deref_unlink_file(Hashmap **known_files, const char *fn, const char *root) {
×
641
        _cleanup_free_ char *path = NULL;
×
642
        int r;
×
643

644
        assert(known_files);
×
645

646
        /* just gracefully ignore this. This way the caller doesn't
647
           have to verify whether the bootloader entry is relevant */
648
        if (!fn || !root)
×
649
                return;
650

651
        r = ref_file(known_files, fn, -1);
×
652
        if (r < 0)
×
653
                return (void) log_warning_errno(r, "Failed to deref \"%s\", ignoring: %m", fn);
×
654
        if (r > 0)
×
655
                return;
656

657
        if (arg_dry_run) {
×
658
                r = chase_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, &path);
×
659
                if (r < 0)
×
660
                        log_info_errno(r, "Unable to determine whether \"%s\" exists, ignoring: %m", fn);
×
661
                else
662
                        log_info("Would remove \"%s\"", path);
×
663
                return;
×
664
        }
665

666
        r = chase_and_unlink(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, 0, &path);
×
667
        if (r >= 0)
×
668
                log_info("Removed \"%s\"", path);
×
669
        else if (r != -ENOENT)
×
670
                return (void) log_warning_errno(r, "Failed to remove \"%s\", ignoring: %m", fn);
×
671

672
        _cleanup_free_ char *d = NULL;
×
673
        if (path_extract_directory(fn, &d) >= 0 && !path_equal(d, "/")) {
×
674
                r = chase_and_unlink(d, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, AT_REMOVEDIR, NULL);
×
675
                if (r < 0 && !IN_SET(r, -ENOTEMPTY, -ENOENT))
×
676
                        log_warning_errno(r, "Failed to remove directory \"%s\", ignoring: %m", d);
×
677
        }
678
}
679

680
static int count_known_files(const BootConfig *config, const char* root, Hashmap **ret_known_files) {
×
681
        _cleanup_hashmap_free_ Hashmap *known_files = NULL;
×
682
        int r;
×
683

684
        assert(config);
×
685
        assert(ret_known_files);
×
686

687
        for (size_t i = 0; i < config->n_entries; i++) {
×
688
                const BootEntry *e = config->entries + i;
×
689

690
                if (!path_equal(e->root, root))
×
691
                        continue;
×
692

693
                r = ref_file(&known_files, e->kernel, +1);
×
694
                if (r < 0)
×
695
                        return r;
696
                r = ref_file(&known_files, e->efi, +1);
×
697
                if (r < 0)
×
698
                        return r;
699
                STRV_FOREACH(s, e->initrd) {
×
700
                        r = ref_file(&known_files, *s, +1);
×
701
                        if (r < 0)
×
702
                                return r;
703
                }
704
                r = ref_file(&known_files, e->device_tree, +1);
×
705
                if (r < 0)
×
706
                        return r;
707
                STRV_FOREACH(s, e->device_tree_overlay) {
×
708
                        r = ref_file(&known_files, *s, +1);
×
709
                        if (r < 0)
×
710
                                return r;
711
                }
712
        }
713

714
        *ret_known_files = TAKE_PTR(known_files);
×
715

716
        return 0;
×
717
}
718

719
static int boot_config_find_in(const BootConfig *config, const char *root, const char *id) {
×
720
        assert(config);
×
721

722
        if (!root || !id)
×
723
                return -ENOENT;
724

725
        for (size_t i = 0; i < config->n_entries; i++)
×
726
                if (path_equal(config->entries[i].root, root) &&
×
727
                    fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0)
×
728
                        return i;
×
729

730
        return -ENOENT;
731
}
732

733
static int unlink_entry(const BootConfig *config, const char *root, const char *id) {
×
734
        _cleanup_hashmap_free_ Hashmap *known_files = NULL;
×
735
        const BootEntry *e = NULL;
×
736
        int r;
×
737

738
        assert(config);
×
739

740
        r = count_known_files(config, root, &known_files);
×
741
        if (r < 0)
×
742
                return log_error_errno(r, "Failed to count files in %s: %m", root);
×
743

744
        r = boot_config_find_in(config, root, id);
×
745
        if (r < 0)
×
746
                return r;
747

748
        if (r == config->default_entry)
×
749
                log_warning("%s is the default boot entry", id);
×
750
        if (r == config->selected_entry)
×
751
                log_warning("%s is the selected boot entry", id);
×
752

753
        e = &config->entries[r];
×
754

755
        deref_unlink_file(&known_files, e->kernel, e->root);
×
756
        deref_unlink_file(&known_files, e->efi, e->root);
×
757
        STRV_FOREACH(s, e->initrd)
×
758
                deref_unlink_file(&known_files, *s, e->root);
×
759
        deref_unlink_file(&known_files, e->device_tree, e->root);
×
760
        STRV_FOREACH(s, e->device_tree_overlay)
×
761
                deref_unlink_file(&known_files, *s, e->root);
×
762

763
        if (arg_dry_run)
×
764
                log_info("Would remove \"%s\"", e->path);
×
765
        else {
766
                r = chase_and_unlink(e->path, root, CHASE_PROHIBIT_SYMLINKS, 0, NULL);
×
767
                if (r < 0)
×
768
                        return log_error_errno(r, "Failed to remove \"%s\": %m", e->path);
×
769

770
                log_info("Removed %s", e->path);
×
771
        }
772

773
        return 0;
774
}
775

776
static int list_remove_orphaned_file(
×
777
                RecurseDirEvent event,
778
                const char *path,
779
                int dir_fd,
780
                int inode_fd,
781
                const struct dirent *de,
782
                const struct statx *sx,
783
                void *userdata) {
784

785
        Hashmap *known_files = userdata;
×
786

787
        assert(path);
×
788

789
        if (event != RECURSE_DIR_ENTRY)
×
790
                return RECURSE_DIR_CONTINUE;
791

792
        if (hashmap_get(known_files, path))
×
793
                return RECURSE_DIR_CONTINUE; /* keep! */
794

795
        if (arg_dry_run)
×
796
                log_info("Would remove %s", path);
×
797
        else if (unlinkat(dir_fd, de->d_name, 0) < 0)
×
798
                log_warning_errno(errno, "Failed to remove \"%s\", ignoring: %m", path);
×
799
        else
800
                log_info("Removed %s", path);
×
801

802
        return RECURSE_DIR_CONTINUE;
803
}
804

805
static int cleanup_orphaned_files(
×
806
                const BootConfig *config,
807
                const char *root) {
808

809
        _cleanup_hashmap_free_ Hashmap *known_files = NULL;
×
810
        _cleanup_free_ char *full = NULL, *p = NULL;
×
811
        _cleanup_close_ int dir_fd = -EBADF;
×
812
        int r;
×
813

814
        assert(config);
×
815
        assert(root);
×
816

817
        log_info("Cleaning %s", root);
×
818

819
        r = settle_entry_token();
×
820
        if (r < 0)
×
821
                return r;
822

823
        r = count_known_files(config, root, &known_files);
×
824
        if (r < 0)
×
825
                return log_error_errno(r, "Failed to count files in %s: %m", root);
×
826

827
        dir_fd = chase_and_open(arg_entry_token, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
×
828
                        O_DIRECTORY|O_CLOEXEC, &full);
829
        if (dir_fd == -ENOENT)
×
830
                return 0;
831
        if (dir_fd < 0)
×
832
                return log_error_errno(dir_fd, "Failed to open '%s/%s': %m", root, arg_entry_token);
×
833

834
        p = path_join("/", arg_entry_token);
×
835
        if (!p)
×
836
                return log_oom();
×
837

838
        r = recurse_dir(dir_fd, p, 0, UINT_MAX, RECURSE_DIR_SORT, list_remove_orphaned_file, known_files);
×
839
        if (r < 0)
×
840
                return log_error_errno(r, "Failed to cleanup %s: %m", full);
×
841

842
        return r;
843
}
844

845
int verb_list(int argc, char *argv[], void *userdata) {
12✔
846
        _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
12✔
847
        dev_t esp_devid = 0, xbootldr_devid = 0;
12✔
848
        int r;
12✔
849

850
        /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
851
         * things: turn off logging about access errors and turn off potentially privileged device probing.
852
         * Here we're interested in the latter but not the former, hence request the mode, and log about
853
         * EACCES. */
854

855
        r = acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid);
12✔
856
        if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
12✔
857
                return log_error_errno(r, "Failed to determine ESP location: %m");
×
858
        if (r < 0)
12✔
859
                return r;
860

861
        r = acquire_xbootldr(/* unprivileged_mode= */ -1, NULL, &xbootldr_devid);
12✔
862
        if (r == -EACCES)
12✔
863
                return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
×
864
        if (r < 0)
12✔
865
                return r;
866

867
        r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
12✔
868
        if (r < 0)
12✔
869
                return r;
870

871
        if (config.n_entries == 0 && !sd_json_format_enabled(arg_json_format_flags)) {
12✔
872
                log_info("No boot loader entries found.");
4✔
873
                return 0;
4✔
874
        }
875

876
        if (streq(argv[0], "list")) {
8✔
877
                pager_open(arg_pager_flags);
8✔
878
                return show_boot_entries(&config, arg_json_format_flags);
8✔
879
        } else if (streq(argv[0], "cleanup")) {
×
880
                if (arg_xbootldr_path && xbootldr_devid != esp_devid)
×
881
                        cleanup_orphaned_files(&config, arg_xbootldr_path);
×
882
                return cleanup_orphaned_files(&config, arg_esp_path);
×
883
        } else {
884
                assert(streq(argv[0], "unlink"));
×
885
                if (arg_xbootldr_path && xbootldr_devid != esp_devid) {
×
886
                        r = unlink_entry(&config, arg_xbootldr_path, argv[1]);
×
887
                        if (r == 0 || r != -ENOENT)
×
888
                                return r;
889
                }
890
                return unlink_entry(&config, arg_esp_path, argv[1]);
×
891
        }
892
}
893

894
int verb_unlink(int argc, char *argv[], void *userdata) {
×
895
        return verb_list(argc, argv, userdata);
×
896
}
897

898
int vl_method_list_boot_entries(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
1✔
899
        _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
×
900
        dev_t esp_devid = 0, xbootldr_devid = 0;
1✔
901
        int r;
1✔
902

903
        assert(link);
1✔
904

905
        r = sd_varlink_dispatch(link, parameters, /* dispatch_table = */ NULL, /* userdata = */ NULL);
1✔
906
        if (r != 0)
1✔
907
                return r;
908

909
        if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
1✔
910
                return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
×
911

912
        r = acquire_esp(/* unprivileged_mode= */ false,
1✔
913
                        /* graceful= */ false,
914
                        /* ret_part= */ NULL,
915
                        /* ret_pstart= */ NULL,
916
                        /* ret_psize= */ NULL,
917
                        /* ret_uuid=*/ NULL,
918
                        &esp_devid);
919
        if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
1✔
920
                return log_error_errno(r, "Failed to determine ESP location: %m");
×
921
        if (r < 0)
1✔
922
                return r;
923

924
        r = acquire_xbootldr(
1✔
925
                        /* unprivileged_mode= */ false,
926
                        /* ret_uuid= */ NULL,
927
                        &xbootldr_devid);
928
        if (r == -EACCES)
1✔
929
                return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
×
930
        if (r < 0)
1✔
931
                return r;
932

933
        r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
1✔
934
        if (r < 0)
1✔
935
                return r;
936

937
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *previous = NULL;
1✔
938
        for (size_t i = 0; i < config.n_entries; i++) {
4✔
939
                if (previous) {
3✔
940
                        r = sd_varlink_notifybo(link, SD_JSON_BUILD_PAIR_VARIANT("entry", previous));
2✔
941
                        if (r < 0)
2✔
942
                                return r;
943

944
                        previous = sd_json_variant_unref(previous);
2✔
945
                }
946

947
                r = boot_entry_to_json(&config, i, &previous);
3✔
948
                if (r < 0)
3✔
949
                        return r;
950
        }
951

952
        if (!previous)
1✔
953
                return sd_varlink_error(link, "io.systemd.BootControl.NoSuchBootEntry", NULL);
×
954

955
        return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_VARIANT("entry", previous));
1✔
956
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc