mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
Merge pull request #25784 from poettering/bootctl-split
bootctl: split up bootctl.c into multiple files
This commit is contained in:
commit
5ac1e0eff3
17
meson.build
17
meson.build
@ -2605,7 +2605,22 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
|
||||
|
||||
exe = executable(
|
||||
'bootctl',
|
||||
'src/boot/bootctl.c',
|
||||
['src/boot/bootctl.c',
|
||||
'src/boot/bootctl.h',
|
||||
'src/boot/bootctl-install.c',
|
||||
'src/boot/bootctl-install.h',
|
||||
'src/boot/bootctl-random-seed.c',
|
||||
'src/boot/bootctl-random-seed.h',
|
||||
'src/boot/bootctl-reboot-to-firmware.c',
|
||||
'src/boot/bootctl-reboot-to-firmware.h',
|
||||
'src/boot/bootctl-set-efivar.c',
|
||||
'src/boot/bootctl-set-efivar.h',
|
||||
'src/boot/bootctl-status.c',
|
||||
'src/boot/bootctl-status.h',
|
||||
'src/boot/bootctl-systemd-efi-options.c',
|
||||
'src/boot/bootctl-systemd-efi-options.h',
|
||||
'src/boot/bootctl-util.c',
|
||||
'src/boot/bootctl-util.h'],
|
||||
include_directories : includes,
|
||||
link_with : [boot_link_with],
|
||||
dependencies : [libblkid,
|
||||
|
1157
src/boot/bootctl-install.c
Normal file
1157
src/boot/bootctl-install.c
Normal file
File diff suppressed because it is too large
Load Diff
6
src/boot/bootctl-install.h
Normal file
6
src/boot/bootctl-install.h
Normal file
@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int verb_install(int argc, char *argv[], void *userdata);
|
||||
int verb_remove(int argc, char *argv[], void *userdata);
|
||||
int verb_is_installed(int argc, char *argv[], void *userdata);
|
151
src/boot/bootctl-random-seed.c
Normal file
151
src/boot/bootctl-random-seed.c
Normal file
@ -0,0 +1,151 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-random-seed.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "efi-api.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "find-esp.h"
|
||||
#include "fs-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "path-util.h"
|
||||
#include "random-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "umask-util.h"
|
||||
|
||||
int install_random_seed(const char *esp) {
|
||||
_cleanup_(unlink_and_freep) char *tmp = NULL;
|
||||
uint8_t buffer[RANDOM_EFI_SEED_SIZE];
|
||||
_cleanup_free_ char *path = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
size_t token_size;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
assert(esp);
|
||||
|
||||
path = path_join(esp, "/loader/random-seed");
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
r = crypto_random_bytes(buffer, sizeof(buffer));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire random seed: %m");
|
||||
|
||||
/* Normally create_subdirs() should already have created everything we need, but in case "bootctl
|
||||
* random-seed" is called we want to just create the minimum we need for it, and not the full
|
||||
* list. */
|
||||
r = mkdir_parents(path, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create parent directory for %s: %m", path);
|
||||
|
||||
r = tempfn_random(path, "bootctl", &tmp);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
|
||||
if (fd < 0) {
|
||||
tmp = mfree(tmp);
|
||||
return log_error_errno(fd, "Failed to open random seed file for writing: %m");
|
||||
}
|
||||
|
||||
n = write(fd, buffer, sizeof(buffer));
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to write random seed file: %m");
|
||||
if ((size_t) n != sizeof(buffer))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
|
||||
|
||||
if (rename(tmp, path) < 0)
|
||||
return log_error_errno(r, "Failed to move random seed file into place: %m");
|
||||
|
||||
tmp = mfree(tmp);
|
||||
|
||||
log_info("Random seed file %s successfully written (%zu bytes).", path, sizeof(buffer));
|
||||
|
||||
if (!arg_touch_variables)
|
||||
return 0;
|
||||
|
||||
if (arg_root) {
|
||||
log_warning("Acting on %s, skipping EFI variable setup.",
|
||||
arg_image ? "image" : "root directory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
log_notice("Not booted with EFI, skipping EFI variable setup.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
|
||||
} else if (r == 0) {
|
||||
log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), NULL, NULL, &token_size);
|
||||
if (r == -ENODATA)
|
||||
log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
|
||||
else if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to test system token validity: %m");
|
||||
} else {
|
||||
if (token_size >= sizeof(buffer)) {
|
||||
/* Let's avoid writes if we can, and initialize this only once. */
|
||||
log_debug("System token already written, not updating.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
|
||||
}
|
||||
|
||||
r = crypto_random_bytes(buffer, sizeof(buffer));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire random seed: %m");
|
||||
|
||||
/* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
|
||||
* and possibly get identification information or too much insight into the kernel's entropy pool
|
||||
* state. */
|
||||
WITH_UMASK(0077) {
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
|
||||
if (r < 0) {
|
||||
if (!arg_graceful)
|
||||
return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
|
||||
|
||||
if (r == -EINVAL)
|
||||
log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
|
||||
else
|
||||
log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
|
||||
} else
|
||||
log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verb_random_seed(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
r = find_esp_and_warn(arg_root, arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL);
|
||||
if (r == -ENOKEY) {
|
||||
/* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
|
||||
if (!arg_graceful)
|
||||
return log_error_errno(r, "Unable to find ESP.");
|
||||
|
||||
log_notice("No ESP found, not initializing random seed.");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = install_random_seed(arg_esp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sync_everything();
|
||||
return 0;
|
||||
}
|
6
src/boot/bootctl-random-seed.h
Normal file
6
src/boot/bootctl-random-seed.h
Normal file
@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int install_random_seed(const char *esp);
|
||||
|
||||
int verb_random_seed(int argc, char *argv[], void *userdata);
|
38
src/boot/bootctl-reboot-to-firmware.c
Normal file
38
src/boot/bootctl-reboot-to-firmware.c
Normal file
@ -0,0 +1,38 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "bootctl-reboot-to-firmware.h"
|
||||
#include "efi-api.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
if (argc < 2) {
|
||||
r = efi_get_reboot_to_firmware();
|
||||
if (r > 0) {
|
||||
puts("active");
|
||||
return EXIT_SUCCESS; /* success */
|
||||
}
|
||||
if (r == 0) {
|
||||
puts("supported");
|
||||
return 1; /* recognizable error #1 */
|
||||
}
|
||||
if (r == -EOPNOTSUPP) {
|
||||
puts("not supported");
|
||||
return 2; /* recognizable error #2 */
|
||||
}
|
||||
|
||||
log_error_errno(r, "Failed to query reboot-to-firmware state: %m");
|
||||
return 3; /* other kind of error */
|
||||
} else {
|
||||
r = parse_boolean(argv[1]);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse argument: %s", argv[1]);
|
||||
|
||||
r = efi_set_reboot_to_firmware(r);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set reboot-to-firmware option: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
3
src/boot/bootctl-reboot-to-firmware.h
Normal file
3
src/boot/bootctl-reboot-to-firmware.h
Normal file
@ -0,0 +1,3 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
int verb_reboot_to_firmware(int argc, char *argv[], void *userdata);
|
149
src/boot/bootctl-set-efivar.c
Normal file
149
src/boot/bootctl-set-efivar.c
Normal file
@ -0,0 +1,149 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <uchar.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-set-efivar.h"
|
||||
#include "efivars.h"
|
||||
#include "stdio-util.h"
|
||||
#include "utf8.h"
|
||||
#include "virt.h"
|
||||
|
||||
static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
|
||||
char utf8[DECIMAL_STR_MAX(usec_t)];
|
||||
char16_t *encoded;
|
||||
usec_t timeout;
|
||||
int r;
|
||||
|
||||
assert(arg1);
|
||||
assert(ret_timeout);
|
||||
assert(ret_timeout_size);
|
||||
|
||||
if (streq(arg1, "menu-force"))
|
||||
timeout = USEC_INFINITY;
|
||||
else if (streq(arg1, "menu-hidden"))
|
||||
timeout = 0;
|
||||
else {
|
||||
r = parse_time(arg1, &timeout, USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
|
||||
if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
|
||||
log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
|
||||
}
|
||||
|
||||
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
|
||||
|
||||
encoded = utf8_to_utf16(utf8, strlen(utf8));
|
||||
if (!encoded)
|
||||
return log_oom();
|
||||
|
||||
*ret_timeout = encoded;
|
||||
*ret_timeout_size = char16_strlen(encoded) * 2 + 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) {
|
||||
char16_t *encoded = NULL;
|
||||
int r;
|
||||
|
||||
assert(arg1);
|
||||
assert(ret_target);
|
||||
assert(ret_target_size);
|
||||
|
||||
if (streq(arg1, "@current")) {
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m");
|
||||
|
||||
} else if (streq(arg1, "@oneshot")) {
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
|
||||
|
||||
} else if (streq(arg1, "@default")) {
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
|
||||
|
||||
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
|
||||
else {
|
||||
encoded = utf8_to_utf16(arg1, strlen(arg1));
|
||||
if (!encoded)
|
||||
return log_oom();
|
||||
|
||||
*ret_target = encoded;
|
||||
*ret_target_size = char16_strlen(encoded) * 2 + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verb_set_efivar(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
if (arg_root)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Acting on %s, skipping EFI variable setup.",
|
||||
arg_image ? "image" : "root directory");
|
||||
|
||||
if (!is_efi_boot())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Not booted with UEFI.");
|
||||
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_error_errno(errno, "Not booted with a supported boot loader.");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
|
||||
}
|
||||
|
||||
if (detect_container() > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"'%s' operation not supported in a container.",
|
||||
argv[0]);
|
||||
|
||||
if (!arg_touch_variables)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' operation cannot be combined with --no-variables.",
|
||||
argv[0]);
|
||||
|
||||
const char *variable;
|
||||
int (* arg_parser)(const char *, char16_t **, size_t *);
|
||||
|
||||
if (streq(argv[0], "set-default")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
|
||||
arg_parser = parse_loader_entry_target_arg;
|
||||
} else if (streq(argv[0], "set-oneshot")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
|
||||
arg_parser = parse_loader_entry_target_arg;
|
||||
} else if (streq(argv[0], "set-timeout")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
|
||||
arg_parser = parse_timeout;
|
||||
} else if (streq(argv[0], "set-timeout-oneshot")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
|
||||
arg_parser = parse_timeout;
|
||||
} else
|
||||
assert_not_reached();
|
||||
|
||||
if (isempty(argv[1])) {
|
||||
r = efi_set_variable(variable, NULL, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable);
|
||||
} else {
|
||||
_cleanup_free_ char16_t *value = NULL;
|
||||
size_t value_size = 0;
|
||||
|
||||
r = arg_parser(argv[1], &value, &value_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = efi_set_variable(variable, value, value_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
4
src/boot/bootctl-set-efivar.h
Normal file
4
src/boot/bootctl-set-efivar.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int verb_set_efivar(int argc, char *argv[], void *userdata);
|
537
src/boot/bootctl-status.c
Normal file
537
src/boot/bootctl-status.c
Normal file
@ -0,0 +1,537 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-status.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "bootspec.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "devnum-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efi-api.h"
|
||||
#include "efi-loader.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "find-esp.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
static int boot_config_load_and_select(
|
||||
BootConfig *config,
|
||||
const char *esp_path,
|
||||
dev_t esp_devid,
|
||||
const char *xbootldr_path,
|
||||
dev_t xbootldr_devid) {
|
||||
|
||||
int r;
|
||||
|
||||
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
|
||||
* find the same entries twice. */
|
||||
bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
|
||||
|
||||
r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!arg_root) {
|
||||
_cleanup_strv_free_ char **efi_entries = NULL;
|
||||
|
||||
r = efi_loader_get_entries(&efi_entries);
|
||||
if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
|
||||
log_debug_errno(r, "Boot loader reported no entries.");
|
||||
else if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
|
||||
else
|
||||
(void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
|
||||
}
|
||||
|
||||
return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
|
||||
}
|
||||
|
||||
static int status_entries(
|
||||
const BootConfig *config,
|
||||
const char *esp_path,
|
||||
sd_id128_t esp_partition_uuid,
|
||||
const char *xbootldr_path,
|
||||
sd_id128_t xbootldr_partition_uuid) {
|
||||
|
||||
sd_id128_t dollar_boot_partition_uuid;
|
||||
const char *dollar_boot_path;
|
||||
int r;
|
||||
|
||||
assert(config);
|
||||
assert(esp_path || xbootldr_path);
|
||||
|
||||
if (xbootldr_path) {
|
||||
dollar_boot_path = xbootldr_path;
|
||||
dollar_boot_partition_uuid = xbootldr_partition_uuid;
|
||||
} else {
|
||||
dollar_boot_path = esp_path;
|
||||
dollar_boot_partition_uuid = esp_partition_uuid;
|
||||
}
|
||||
|
||||
printf("%sBoot Loader Entries:%s\n"
|
||||
" $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
|
||||
if (!sd_id128_is_null(dollar_boot_partition_uuid))
|
||||
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
|
||||
SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
|
||||
printf("\n\n");
|
||||
|
||||
if (config->default_entry < 0)
|
||||
printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
|
||||
else {
|
||||
printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
r = show_boot_entry(
|
||||
boot_config_default_entry(config),
|
||||
/* show_as_default= */ false,
|
||||
/* show_as_selected= */ false,
|
||||
/* show_discovered= */ false);
|
||||
if (r > 0)
|
||||
/* < 0 is already logged by the function itself, let's just emit an extra warning if
|
||||
the default entry is broken */
|
||||
printf("\nWARNING: default boot entry is broken\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
|
||||
_cleanup_free_ char *title = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
sd_id128_t partition;
|
||||
bool active;
|
||||
int r;
|
||||
|
||||
assert(n_printed);
|
||||
|
||||
r = efi_get_boot_option(id, &title, &partition, &path, &active);
|
||||
if (r == -ENOENT) {
|
||||
log_debug_errno(r, "Boot option 0x%04X referenced but missing, ignoring: %m", id);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read boot option 0x%04X: %m", id);
|
||||
|
||||
/* print only configured entries with partition information */
|
||||
if (!path || sd_id128_is_null(partition)) {
|
||||
log_debug("Ignoring boot entry 0x%04X without partition information.", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
efi_tilt_backslashes(path);
|
||||
|
||||
if (*n_printed == 0) /* Print section title before first entry */
|
||||
printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
|
||||
printf(" ID: 0x%04X\n", id);
|
||||
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
|
||||
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
|
||||
SD_ID128_FORMAT_VAL(partition));
|
||||
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
|
||||
printf("\n");
|
||||
|
||||
(*n_printed)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int status_variables(void) {
|
||||
_cleanup_free_ uint16_t *options = NULL, *order = NULL;
|
||||
int n_options, n_order, n_printed = 0;
|
||||
|
||||
n_options = efi_get_boot_options(&options);
|
||||
if (n_options == -ENOENT)
|
||||
return log_error_errno(n_options,
|
||||
"Failed to access EFI variables, efivarfs"
|
||||
" needs to be available at /sys/firmware/efi/efivars/.");
|
||||
if (n_options < 0)
|
||||
return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
|
||||
|
||||
n_order = efi_get_boot_order(&order);
|
||||
if (n_order == -ENOENT)
|
||||
n_order = 0;
|
||||
else if (n_order < 0)
|
||||
return log_error_errno(n_order, "Failed to read EFI boot order: %m");
|
||||
|
||||
/* print entries in BootOrder first */
|
||||
for (int i = 0; i < n_order; i++)
|
||||
(void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
|
||||
|
||||
/* print remaining entries */
|
||||
for (int i = 0; i < n_options; i++) {
|
||||
for (int j = 0; j < n_order; j++)
|
||||
if (options[i] == order[j])
|
||||
goto next_option;
|
||||
|
||||
(void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
|
||||
|
||||
next_option:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n_printed == 0)
|
||||
printf("No boot loaders listed in EFI Variables.\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enumerate_binaries(
|
||||
const char *esp_path,
|
||||
const char *path,
|
||||
const char *prefix,
|
||||
char **previous,
|
||||
bool *is_first) {
|
||||
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int c = 0, r;
|
||||
|
||||
assert(esp_path);
|
||||
assert(path);
|
||||
assert(previous);
|
||||
assert(is_first);
|
||||
|
||||
r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read \"%s/%s\": %m", esp_path, path);
|
||||
|
||||
FOREACH_DIRENT(de, d, break) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
if (!endswith_no_case(de->d_name, ".efi"))
|
||||
continue;
|
||||
|
||||
if (prefix && !startswith_no_case(de->d_name, prefix))
|
||||
continue;
|
||||
|
||||
fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
|
||||
|
||||
r = get_file_version(fd, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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 */
|
||||
printf(" %s %s%s\n",
|
||||
*is_first ? "File:" : " ",
|
||||
special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
|
||||
*is_first = false;
|
||||
*previous = mfree(*previous);
|
||||
}
|
||||
|
||||
/* Do not output this entry immediately, but store what should be printed in a state
|
||||
* variable, because we only will know the tree glyph to print (branch or final edge) once we
|
||||
* read one more entry */
|
||||
if (r > 0)
|
||||
r = asprintf(previous, "/%s/%s (%s%s%s)", path, de->d_name, ansi_highlight(), v, ansi_normal());
|
||||
else
|
||||
r = asprintf(previous, "/%s/%s", path, de->d_name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int status_binaries(const char *esp_path, sd_id128_t partition) {
|
||||
_cleanup_free_ char *last = NULL;
|
||||
bool is_first = true;
|
||||
int r, k;
|
||||
|
||||
printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
if (!esp_path) {
|
||||
printf(" ESP: Cannot find or access mount point of ESP.\n\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
printf(" ESP: %s", esp_path);
|
||||
if (!sd_id128_is_null(partition))
|
||||
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
|
||||
printf("\n");
|
||||
|
||||
r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
|
||||
if (r < 0) {
|
||||
printf("\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
|
||||
if (k < 0) {
|
||||
printf("\n");
|
||||
return k;
|
||||
}
|
||||
|
||||
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 */
|
||||
printf(" %s %s%s\n",
|
||||
is_first ? "File:" : " ",
|
||||
special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
|
||||
|
||||
if (r == 0 && !arg_quiet)
|
||||
log_info("systemd-boot not installed in ESP.");
|
||||
if (k == 0 && !arg_quiet)
|
||||
log_info("No default/fallback boot loader installed in ESP.");
|
||||
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_efi_var(const char *variable, char **ret) {
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_string(variable, ret);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read EFI variable %s: %m", variable);
|
||||
}
|
||||
|
||||
static void print_yes_no_line(bool first, bool good, const char *name) {
|
||||
printf("%s%s %s\n",
|
||||
first ? " Features: " : " ",
|
||||
COLOR_MARK_BOOL(good),
|
||||
name);
|
||||
}
|
||||
|
||||
int verb_status(int argc, char *argv[], void *userdata) {
|
||||
sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
|
||||
dev_t esp_devid = 0, xbootldr_devid = 0;
|
||||
int r, k;
|
||||
|
||||
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid, &esp_devid);
|
||||
if (arg_print_esp_path) {
|
||||
if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
|
||||
* error the find_esp_and_warn() won't log on its own) */
|
||||
return log_error_errno(r, "Failed to determine ESP location: %m");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
puts(arg_esp_path);
|
||||
}
|
||||
|
||||
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid, &xbootldr_devid);
|
||||
if (arg_print_dollar_boot_path) {
|
||||
if (r == -EACCES)
|
||||
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *path = arg_dollar_boot_path();
|
||||
if (!path)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Failed to determine XBOOTLDR location: %m");
|
||||
|
||||
puts(path);
|
||||
}
|
||||
|
||||
if (arg_print_esp_path || arg_print_dollar_boot_path)
|
||||
return 0;
|
||||
|
||||
r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
|
||||
* show what we can show */
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
if (!arg_root && is_efi_boot()) {
|
||||
static const struct {
|
||||
uint64_t flag;
|
||||
const char *name;
|
||||
} loader_flags[] = {
|
||||
{ EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
|
||||
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
|
||||
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
|
||||
{ EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
|
||||
{ EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
|
||||
{ EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
|
||||
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
|
||||
{ EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
|
||||
{ EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
|
||||
{ EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
|
||||
{ EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
|
||||
};
|
||||
static const struct {
|
||||
uint64_t flag;
|
||||
const char *name;
|
||||
} stub_flags[] = {
|
||||
{ EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
|
||||
{ EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
|
||||
{ EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
|
||||
};
|
||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
|
||||
sd_id128_t loader_part_uuid = SD_ID128_NULL;
|
||||
uint64_t loader_features = 0, stub_features = 0;
|
||||
Tpm2Support s;
|
||||
int have;
|
||||
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
(void) efi_stub_get_features(&stub_features);
|
||||
|
||||
if (loader_path)
|
||||
efi_tilt_backslashes(loader_path);
|
||||
|
||||
k = efi_loader_get_device_part_uuid(&loader_part_uuid);
|
||||
if (k < 0 && k != -ENOENT)
|
||||
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
|
||||
|
||||
SecureBootMode secure = efi_get_secure_boot_mode();
|
||||
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
|
||||
printf(" Firmware Arch: %s\n", get_efi_arch());
|
||||
printf(" Secure Boot: %sd (%s)\n",
|
||||
enable_disable(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
|
||||
secure_boot_mode_to_string(secure));
|
||||
|
||||
s = tpm2_support();
|
||||
printf(" TPM2 Support: %s%s%s\n",
|
||||
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
|
||||
(s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
|
||||
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? "yes" :
|
||||
(s & TPM2_SUPPORT_FIRMWARE) ? "firmware only, driver unavailable" :
|
||||
(s & TPM2_SUPPORT_DRIVER) ? "driver only, firmware unavailable" : "no",
|
||||
ansi_normal());
|
||||
|
||||
k = efi_get_reboot_to_firmware();
|
||||
if (k > 0)
|
||||
printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
|
||||
else if (k == 0)
|
||||
printf(" Boot into FW: supported\n");
|
||||
else if (k == -EOPNOTSUPP)
|
||||
printf(" Boot into FW: not supported\n");
|
||||
else {
|
||||
errno = -k;
|
||||
printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
|
||||
|
||||
sd_id128_t bootloader_esp_uuid;
|
||||
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
|
||||
|
||||
print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information");
|
||||
if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) &&
|
||||
!sd_id128_equal(esp_uuid, bootloader_esp_uuid))
|
||||
printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
|
||||
SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
|
||||
SD_ID128_FORMAT_VAL(esp_uuid));
|
||||
|
||||
if (stub) {
|
||||
printf(" Stub: %s\n", stub);
|
||||
for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
|
||||
}
|
||||
if (!sd_id128_is_null(loader_part_uuid))
|
||||
printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
|
||||
SD_ID128_FORMAT_VAL(loader_part_uuid));
|
||||
else
|
||||
printf(" ESP: n/a\n");
|
||||
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
|
||||
printf("\n");
|
||||
|
||||
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
|
||||
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
|
||||
printf(" System Token: %s\n", have ? "set" : "not set");
|
||||
|
||||
if (arg_esp_path) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
p = path_join(arg_esp_path, "/loader/random-seed");
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
have = access(p, F_OK) >= 0;
|
||||
printf(" Exists: %s\n", yes_no(have));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
} else
|
||||
printf("%sSystem:%s\n"
|
||||
"Not booted with EFI\n\n",
|
||||
ansi_underline(), ansi_normal());
|
||||
|
||||
if (arg_esp_path) {
|
||||
k = status_binaries(arg_esp_path, esp_uuid);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
if (!arg_root && is_efi_boot()) {
|
||||
k = status_variables();
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
if (arg_esp_path || arg_xbootldr_path) {
|
||||
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
|
||||
|
||||
k = boot_config_load_and_select(&config,
|
||||
arg_esp_path, esp_devid,
|
||||
arg_xbootldr_path, xbootldr_devid);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
else {
|
||||
k = status_entries(&config,
|
||||
arg_esp_path, esp_uuid,
|
||||
arg_xbootldr_path, xbootldr_uuid);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int verb_list(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
|
||||
dev_t esp_devid = 0, xbootldr_devid = 0;
|
||||
int r;
|
||||
|
||||
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
|
||||
* things: turn off logging about access errors and turn off potentially privileged device probing.
|
||||
* Here we're interested in the latter but not the former, hence request the mode, and log about
|
||||
* EACCES. */
|
||||
|
||||
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid);
|
||||
if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
|
||||
return log_error_errno(r, "Failed to determine ESP location: %m");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL, &xbootldr_devid);
|
||||
if (r == -EACCES)
|
||||
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (config.n_entries == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
|
||||
log_info("No boot loader entries found.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
return show_boot_entries(&config, arg_json_format_flags);
|
||||
}
|
4
src/boot/bootctl-status.h
Normal file
4
src/boot/bootctl-status.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
int verb_status(int argc, char *argv[], void *userdata);
|
||||
int verb_list(int argc, char *argv[], void *userdata);
|
37
src/boot/bootctl-systemd-efi-options.c
Normal file
37
src/boot/bootctl-systemd-efi-options.c
Normal file
@ -0,0 +1,37 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bootctl-systemd-efi-options.h"
|
||||
#include "efi-loader.h"
|
||||
|
||||
int verb_systemd_efi_options(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
if (argc == 1) {
|
||||
_cleanup_free_ char *line = NULL, *new = NULL;
|
||||
|
||||
r = systemd_efi_options_variable(&line);
|
||||
if (r == -ENODATA)
|
||||
log_debug("No SystemdOptions EFI variable present in cache.");
|
||||
else if (r < 0)
|
||||
return log_error_errno(r, "Failed to read SystemdOptions EFI variable from cache: %m");
|
||||
else
|
||||
puts(line);
|
||||
|
||||
r = systemd_efi_options_efivarfs_if_newer(&new);
|
||||
if (r == -ENODATA) {
|
||||
if (line)
|
||||
log_notice("Note: SystemdOptions EFI variable has been removed since boot.");
|
||||
} else if (r < 0)
|
||||
log_warning_errno(r, "Failed to check SystemdOptions EFI variable in efivarfs, ignoring: %m");
|
||||
else if (new && !streq_ptr(line, new))
|
||||
log_notice("Note: SystemdOptions EFI variable has been modified since boot. New value: %s",
|
||||
new);
|
||||
} else {
|
||||
r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), argv[1]);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set SystemdOptions EFI variable: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
4
src/boot/bootctl-systemd-efi-options.h
Normal file
4
src/boot/bootctl-systemd-efi-options.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int verb_systemd_efi_options(int argc, char *argv[], void *userdata);
|
111
src/boot/bootctl-util.c
Normal file
111
src/boot/bootctl-util.c
Normal file
@ -0,0 +1,111 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "fileio.h"
|
||||
#include "stat-util.h"
|
||||
#include "sync-util.h"
|
||||
|
||||
int sync_everything(void) {
|
||||
int ret = 0, k;
|
||||
|
||||
if (arg_esp_path) {
|
||||
k = syncfs_path(AT_FDCWD, arg_esp_path);
|
||||
if (k < 0)
|
||||
ret = log_error_errno(k, "Failed to synchronize the ESP '%s': %m", arg_esp_path);
|
||||
}
|
||||
|
||||
if (arg_xbootldr_path) {
|
||||
k = syncfs_path(AT_FDCWD, arg_xbootldr_path);
|
||||
if (k < 0)
|
||||
ret = log_error_errno(k, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *get_efi_arch(void) {
|
||||
/* Detect EFI firmware architecture of the running system. On mixed mode systems, it could be 32bit
|
||||
* while the kernel is running in 64bit. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
_cleanup_free_ char *platform_size = NULL;
|
||||
int r;
|
||||
|
||||
r = read_one_line_file("/sys/firmware/efi/fw_platform_size", &platform_size);
|
||||
if (r == -ENOENT)
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Error reading EFI firmware word size, assuming '%i': %m", __WORDSIZE);
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
}
|
||||
|
||||
if (streq(platform_size, "64"))
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
if (streq(platform_size, "32"))
|
||||
return "ia32";
|
||||
|
||||
log_warning(
|
||||
"Unknown EFI firmware word size '%s', using default word size '%i' instead.",
|
||||
platform_size,
|
||||
__WORDSIZE);
|
||||
#endif
|
||||
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
}
|
||||
|
||||
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
|
||||
int get_file_version(int fd, char **ret) {
|
||||
struct stat st;
|
||||
char *buf;
|
||||
const char *s, *e;
|
||||
char *x = NULL;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat EFI binary: %m");
|
||||
|
||||
r = stat_verify_regular(&st);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "EFI binary is not a regular file: %m");
|
||||
|
||||
if (st.st_size < 27 || file_offset_beyond_memory_size(st.st_size)) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (buf == MAP_FAILED)
|
||||
return log_error_errno(errno, "Failed to memory map EFI binary: %m");
|
||||
|
||||
s = mempmem_safe(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
|
||||
if (!s) {
|
||||
r = -ESRCH;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
e = memmem_safe(s, st.st_size - (s - buf), " ####", 5);
|
||||
if (!e || e - s < 3) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
x = strndup(s, e - s);
|
||||
if (!x) {
|
||||
r = log_oom();
|
||||
goto finish;
|
||||
}
|
||||
r = 1;
|
||||
|
||||
finish:
|
||||
(void) munmap(buf, st.st_size);
|
||||
if (r >= 0)
|
||||
*ret = x;
|
||||
|
||||
return r;
|
||||
}
|
8
src/boot/bootctl-util.h
Normal file
8
src/boot/bootctl-util.h
Normal file
@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int sync_everything(void);
|
||||
|
||||
const char *get_efi_arch(void);
|
||||
|
||||
int get_file_version(int fd, char **ret);
|
2190
src/boot/bootctl.c
2190
src/boot/bootctl.c
File diff suppressed because it is too large
Load Diff
49
src/boot/bootctl.h
Normal file
49
src/boot/bootctl.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "json.h"
|
||||
#include "pager.h"
|
||||
|
||||
typedef enum EntryTokenType {
|
||||
ARG_ENTRY_TOKEN_MACHINE_ID,
|
||||
ARG_ENTRY_TOKEN_OS_IMAGE_ID,
|
||||
ARG_ENTRY_TOKEN_OS_ID,
|
||||
ARG_ENTRY_TOKEN_LITERAL,
|
||||
ARG_ENTRY_TOKEN_AUTO,
|
||||
} EntryTokenType;
|
||||
|
||||
typedef enum InstallSource {
|
||||
ARG_INSTALL_SOURCE_IMAGE,
|
||||
ARG_INSTALL_SOURCE_HOST,
|
||||
ARG_INSTALL_SOURCE_AUTO,
|
||||
} InstallSource;
|
||||
|
||||
extern char *arg_esp_path;
|
||||
extern char *arg_xbootldr_path;
|
||||
extern bool arg_print_esp_path;
|
||||
extern bool arg_print_dollar_boot_path;
|
||||
extern bool arg_touch_variables;
|
||||
extern PagerFlags arg_pager_flags;
|
||||
extern bool arg_graceful;
|
||||
extern bool arg_quiet;
|
||||
extern int arg_make_entry_directory; /* tri-state: < 0 for automatic logic */
|
||||
extern sd_id128_t arg_machine_id;
|
||||
extern char *arg_install_layout;
|
||||
extern EntryTokenType arg_entry_token_type;
|
||||
extern char *arg_entry_token;
|
||||
extern JsonFormatFlags arg_json_format_flags;
|
||||
extern bool arg_arch_all;
|
||||
extern char *arg_root;
|
||||
extern char *arg_image;
|
||||
extern InstallSource arg_install_source;
|
||||
extern char *arg_efi_boot_option_description;
|
||||
|
||||
static inline const char *arg_dollar_boot_path(void) {
|
||||
/* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
|
||||
return arg_xbootldr_path ?: arg_esp_path;
|
||||
}
|
||||
|
||||
int acquire_esp(bool unprivileged_mode, bool graceful, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
||||
int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
Loading…
Reference in New Issue
Block a user