diff options
author | Michael Biebl <biebl@debian.org> | 2018-12-21 22:06:22 +0100 |
---|---|---|
committer | Michael Biebl <biebl@debian.org> | 2018-12-21 22:06:22 +0100 |
commit | 6e866b331d7cd4a5e0759dd160dea6edabd3678e (patch) | |
tree | 4d24c1ffe4ae946f04d8910956090e8d13aecd9a /src/boot/bootctl.c | |
parent | b012e92123bdc9fa10c2f079ec5bd9313b23e21a (diff) | |
download | systemd-6e866b331d7cd4a5e0759dd160dea6edabd3678e.tar.gz |
New upstream version 240
Diffstat (limited to 'src/boot/bootctl.c')
-rw-r--r-- | src/boot/bootctl.c | 383 |
1 files changed, 249 insertions, 134 deletions
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 2832a39dd7..dc2fd96628 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -29,13 +29,18 @@ #include "fileio.h" #include "fs-util.h" #include "locale-util.h" +#include "main-func.h" +#include "pager.h" #include "parse-util.h" +#include "pretty-print.h" #include "rm-rf.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" +#include "tmpfile-util.h" #include "umask-util.h" +#include "utf8.h" #include "util.h" #include "verbs.h" #include "virt.h" @@ -43,6 +48,9 @@ static char *arg_path = NULL; static bool arg_print_path = false; static bool arg_touch_variables = true; +static PagerFlags arg_pager_flags = 0; + +STATIC_DESTRUCTOR_REGISTER(arg_path, freep); static int acquire_esp( bool unprivileged_mode, @@ -155,9 +163,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char if (r < 0) return r; if (r > 0) - printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v); + printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal()); else - printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name); + printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name); c++; } @@ -167,7 +175,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char static int status_binaries(const char *esp_path, sd_id128_t partition) { int r; - printf("Boot Loader Binaries:\n"); + printf("Available Boot Loaders on ESP:\n"); if (!esp_path) { printf(" ESP: Cannot find or access mount point of ESP.\n\n"); @@ -181,13 +189,13 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) { r = enumerate_binaries(esp_path, "EFI/systemd", NULL); if (r == 0) - log_error("systemd-boot not installed in ESP."); + log_info("systemd-boot not installed in ESP."); else if (r < 0) return r; r = enumerate_binaries(esp_path, "EFI/BOOT", "boot"); if (r == 0) - log_error("No default/fallback boot loader installed in ESP."); + log_info("No default/fallback boot loader installed in ESP."); else if (r < 0) return r; @@ -213,20 +221,19 @@ static int print_efi_option(uint16_t id, bool in_order) { efi_tilt_backslashes(path); - printf(" Title: %s\n", strna(title)); + 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/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); - printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path); + printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path); printf("\n"); return 0; } static int status_variables(void) { - int n_options, n_order; _cleanup_free_ uint16_t *options = NULL, *order = NULL; - int i; + int n_options, n_order, i; n_options = efi_get_boot_options(&options); if (n_options == -ENOENT) @@ -240,10 +247,10 @@ static int status_variables(void) { if (n_order == -ENOENT) n_order = 0; else if (n_order < 0) - return log_error_errno(n_order, "Failed to read EFI boot order."); + return log_error_errno(n_order, "Failed to read EFI boot order: %m"); /* print entries in BootOrder first */ - printf("Boot Loader Entries in EFI Variables:\n"); + printf("Boot Loaders Listed in EFI Variables:\n"); for (i = 0; i < n_order; i++) print_efi_option(order[i], true); @@ -264,49 +271,65 @@ static int status_variables(void) { return 0; } -static int status_entries(const char *esp_path, sd_id128_t partition) { - int r; +static int boot_entry_show(const BootEntry *e, bool show_as_default) { + assert(e); + + printf(" title: %s%s%s%s%s%s\n", + ansi_highlight(), + boot_entry_title(e), + ansi_normal(), + ansi_highlight_green(), + show_as_default ? " (default)" : "", + ansi_normal()); + + if (e->id) + printf(" id: %s\n", e->id); + if (e->version) + printf(" version: %s\n", e->version); + if (e->machine_id) + printf(" machine-id: %s\n", e->machine_id); + if (e->architecture) + printf(" architecture: %s\n", e->architecture); + if (e->kernel) + printf(" linux: %s\n", e->kernel); + if (!strv_isempty(e->initrd)) { + _cleanup_free_ char *t; + + t = strv_join(e->initrd, " "); + if (!t) + return log_oom(); + + printf(" initrd: %s\n", t); + } + if (!strv_isempty(e->options)) { + _cleanup_free_ char *t; - _cleanup_(boot_config_free) BootConfig config = {}; + t = strv_join(e->options, " "); + if (!t) + return log_oom(); + + printf(" options: %s\n", t); + } + if (e->device_tree) + printf(" devicetree: %s\n", e->device_tree); - printf("Default Boot Entry:\n"); + return 0; +} + +static int status_entries(const char *esp_path, sd_id128_t partition) { + _cleanup_(boot_config_free) BootConfig config = {}; + int r; r = boot_entries_load_config(esp_path, &config); if (r < 0) - return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", - esp_path); + return r; if (config.default_entry < 0) - printf("%zu entries, no entry suitable as default\n", config.n_entries); + printf("%zu entries, no entry could be determined as default.\n", config.n_entries); else { - const BootEntry *e = &config.entries[config.default_entry]; - - printf(" title: %s\n", boot_entry_title(e)); - if (e->version) - printf(" version: %s\n", e->version); - if (e->kernel) - printf(" linux: %s\n", e->kernel); - if (!strv_isempty(e->initrd)) { - _cleanup_free_ char *t; - - t = strv_join(e->initrd, " "); - if (!t) - return log_oom(); + printf("Default Boot Loader Entry:\n"); - printf(" initrd: %s\n", t); - } - if (!strv_isempty(e->options)) { - _cleanup_free_ char *t; - - t = strv_join(e->options, " "); - if (!t) - return log_oom(); - - printf(" options: %s\n", t); - } - if (e->device_tree) - printf(" devicetree: %s\n", e->device_tree); - puts(""); + boot_entry_show(config.entries + config.default_entry, false); } return 0; @@ -350,18 +373,18 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t r = get_file_version(fd_from, &a); if (r < 0) return r; - if (r == 0) { - log_error("Source file \"%s\" does not carry version information!", from); - return -EINVAL; - } + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Source file \"%s\" does not carry version information!", + from); r = get_file_version(fd_to, &b); if (r < 0) return r; - if (r == 0 || compare_product(a, b) != 0) { - log_notice("Skipping \"%s\", since it's owned by another boot loader.", to); - return -EEXIST; - } + if (r == 0 || compare_product(a, b) != 0) + return log_notice_errno(SYNTHETIC_ERRNO(EEXIST), + "Skipping \"%s\", since it's owned by another boot loader.", + to); if (compare_version(a, b) < 0) { log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to); @@ -800,15 +823,15 @@ static int install_loader_config(const char *esp_path) { if (fd < 0) return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p); - f = fdopen(fd, "we"); + f = fdopen(fd, "w"); if (!f) { safe_close(fd); return log_oom(); } - fprintf(f, "#timeout 3\n"); - fprintf(f, "#console-mode keep\n"); - fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string)); + fprintf(f, "#timeout 3\n" + "#console-mode keep\n" + "default %s-*\n", sd_id128_to_string(machine_id, machine_string)); r = fflush_sync_and_check(f); if (r < 0) @@ -826,23 +849,33 @@ static int install_loader_config(const char *esp_path) { } static int help(int argc, char *argv[], void *userdata) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("bootctl", "1", &link); + if (r < 0) + return log_oom(); - printf("%s [COMMAND] [OPTIONS...]\n" - "\n" + printf("%s [COMMAND] [OPTIONS...]\n\n" "Install, update or remove the systemd-boot EFI boot manager.\n\n" " -h --help Show this help\n" " --version Print version\n" " --path=PATH Path to the EFI System Partition (ESP)\n" " -p --print-path Print path to the EFI partition\n" " --no-variables Don't touch EFI variables\n" - "\n" - "Commands:\n" + " --no-pager Do not pipe output into a pager\n" + "\nBoot Loader Commands:\n" " status Show status of installed systemd-boot and EFI variables\n" - " list List boot entries\n" " install Install systemd-boot to the ESP and EFI variables\n" " update Update systemd-boot in the ESP and EFI variables\n" - " remove Remove systemd-boot from the ESP and EFI variables\n", - program_invocation_short_name); + " remove Remove systemd-boot from the ESP and EFI variables\n" + "\nBoot Loader Entries Commands:\n" + " list List boot loader entries\n" + " set-default ID Set default boot loader entry\n" + " set-oneshot ID Set default boot loader entry, for next boot only\n" + "\nSee the %s for details.\n" + , program_invocation_short_name + , link); return 0; } @@ -852,6 +885,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PATH = 0x100, ARG_VERSION, ARG_NO_VARIABLES, + ARG_NO_PAGER, }; static const struct option options[] = { @@ -860,7 +894,8 @@ static int parse_argv(int argc, char *argv[]) { { "path", required_argument, NULL, ARG_PATH }, { "print-path", no_argument, NULL, 'p' }, { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, - { NULL, 0, NULL, 0 } + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + {} }; int c, r; @@ -892,6 +927,10 @@ static int parse_argv(int argc, char *argv[]) { arg_touch_variables = false; break; + case ARG_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + case '?': return -EINVAL; @@ -931,15 +970,31 @@ static int verb_status(int argc, char *argv[], void *userdata) { 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 */ + (void) pager_open(arg_pager_flags); + if (is_efi_boot()) { + static const struct { + uint64_t flag; + const char *name; + } 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" }, + }; + _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; + size_t i; read_loader_efi_var("LoaderFirmwareType", &fw_type); read_loader_efi_var("LoaderFirmwareInfo", &fw_info); read_loader_efi_var("LoaderInfo", &loader); read_loader_efi_var("StubInfo", &stub); read_loader_efi_var("LoaderImageIdentifier", &loader_path); + (void) efi_loader_get_features(&loader_features); if (loader_path) efi_tilt_backslashes(loader_path); @@ -949,23 +1004,27 @@ static int verb_status(int argc, char *argv[], void *userdata) { r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m"); printf("System:\n"); - printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); + printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal()); + printf(" Secure Boot: %sd\n", enable_disable(is_efi_secure_boot())); + printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user"); + printf("\n"); - k = is_efi_secure_boot(); - if (k < 0) - r = log_warning_errno(k, "Failed to query secure boot status: %m"); - else - printf(" Secure Boot: %sd\n", enable_disable(k)); + printf("Current Boot Loader:\n"); + printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal()); - k = is_efi_secure_boot_setup_mode(); - if (k < 0) - r = log_warning_errno(k, "Failed to query secure boot mode: %m"); - else - printf(" Setup Mode: %s\n", k ? "setup" : "user"); - printf("\n"); + for (i = 0; i < ELEMENTSOF(flags); i++) { + + if (i == 0) + printf(" Features: "); + else + printf(" "); + + if (FLAGS_SET(loader_features, flags[i].flag)) + printf("%s%s%s %s\n", ansi_highlight_green(), special_glyph(SPECIAL_GLYPH_CHECK_MARK), ansi_normal(), flags[i].name); + else + printf("%s%s%s %s\n", ansi_highlight_red(), special_glyph(SPECIAL_GLYPH_CROSS_MARK), ansi_normal(), flags[i].name); + } - printf("Current Loader:\n"); - printf(" Product: %s\n", strna(loader)); if (stub) printf(" Stub: %s\n", stub); if (!sd_id128_is_null(loader_part_uuid)) @@ -973,7 +1032,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { SD_ID128_FORMAT_VAL(loader_part_uuid)); else printf(" ESP: n/a\n"); - printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); + printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path)); printf("\n"); } else printf("System:\n Not booted with EFI\n\n"); @@ -1001,8 +1060,8 @@ static int verb_status(int argc, char *argv[], void *userdata) { static int verb_list(int argc, char *argv[], void *userdata) { _cleanup_(boot_config_free) BootConfig config = {}; + _cleanup_free_ char **found_by_loader = NULL; sd_id128_t uuid = SD_ID128_NULL; - unsigned n; int r; /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn @@ -1017,56 +1076,60 @@ static int verb_list(int argc, char *argv[], void *userdata) { r = boot_entries_load_config(arg_path, &config); if (r < 0) - return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", - arg_path); - - printf("Available boot entries:\n"); - - for (n = 0; n < config.n_entries; n++) { - const BootEntry *e = &config.entries[n]; - - printf(" title: %s%s%s%s%s%s\n", - ansi_highlight(), - boot_entry_title(e), - ansi_normal(), - ansi_highlight_green(), - n == (unsigned) config.default_entry ? " (default)" : "", - ansi_normal()); - if (e->version) - printf(" version: %s\n", e->version); - if (e->machine_id) - printf(" machine-id: %s\n", e->machine_id); - if (e->architecture) - printf(" architecture: %s\n", e->architecture); - if (e->kernel) - printf(" linux: %s\n", e->kernel); - if (!strv_isempty(e->initrd)) { - _cleanup_free_ char *t; - - t = strv_join(e->initrd, " "); - if (!t) - return log_oom(); + return r; - printf(" initrd: %s\n", t); - } - if (!strv_isempty(e->options)) { - _cleanup_free_ char *t; + r = efi_loader_get_entries(&found_by_loader); + if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP)) + log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m"); - t = strv_join(e->options, " "); - if (!t) - return log_oom(); + if (config.n_entries == 0) + log_info("No boot loader entries found."); + else { + size_t n; + + (void) pager_open(arg_pager_flags); + + printf("Boot Loader Entries:\n"); + + for (n = 0; n < config.n_entries; n++) { + r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry); + if (r < 0) + return r; - printf(" options: %s\n", t); + puts(""); + + strv_remove(found_by_loader, config.entries[n].id); } - if (e->device_tree) - printf(" devicetree: %s\n", e->device_tree); + } + + if (!strv_isempty(found_by_loader)) { + char **i; - puts(""); + printf("Automatic/Other Entries Found by Boot Loader:\n\n"); + + STRV_FOREACH(i, found_by_loader) + puts(*i); } return 0; } +static int sync_esp(void) { + _cleanup_close_ int fd = -1; + + if (!arg_path) + return 0; + + fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY); + if (fd < 0) + return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path); + + if (syncfs(fd) < 0) + return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path); + + return 1; +} + static int verb_install(int argc, char *argv[], void *userdata) { sd_id128_t uuid = SD_ID128_NULL; @@ -1093,6 +1156,8 @@ static int verb_install(int argc, char *argv[], void *userdata) { } } + (void) sync_esp(); + if (arg_touch_variables) r = install_variables(arg_path, part, pstart, psize, uuid, @@ -1112,6 +1177,8 @@ static int verb_remove(int argc, char *argv[], void *userdata) { r = remove_binaries(arg_path); + (void) sync_esp(); + if (arg_touch_variables) { int q; @@ -1123,22 +1190,72 @@ static int verb_remove(int argc, char *argv[], void *userdata) { return r; } +static int verb_set_default(int argc, char *argv[], void *userdata) { + const char *name; + int r; + + if (!is_efi_boot()) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Not booted with UEFI."); + + if (access("/sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", 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 --touch-variables=no.", + argv[0]); + + name = streq(argv[0], "set-default") ? "LoaderEntryDefault" : "LoaderEntryOneShot"; + + if (isempty(argv[1])) { + r = efi_set_variable(EFI_VENDOR_LOADER, name, NULL, 0); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to remove EFI variale: %m"); + } else { + _cleanup_free_ char16_t *encoded = NULL; + + encoded = utf8_to_utf16(argv[1], strlen(argv[1])); + if (!encoded) + return log_oom(); + + r = efi_set_variable(EFI_VENDOR_LOADER, name, encoded, char16_strlen(encoded) * 2 + 2); + if (r < 0) + return log_error_errno(r, "Failed to update EFI variable: %m"); + } + + return 0; +} + static int bootctl_main(int argc, char *argv[]) { static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status }, - { "list", VERB_ANY, 1, 0, verb_list }, - { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install }, - { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install }, - { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove }, + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status }, + { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install }, + { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install }, + { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove }, + { "list", VERB_ANY, 1, 0, verb_list }, + { "set-default", 2, 2, VERB_MUST_BE_ROOT, verb_set_default }, + { "set-oneshot", 2, 2, VERB_MUST_BE_ROOT, verb_set_default }, {} }; return dispatch_verb(argc, argv, verbs, NULL); } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { int r; log_parse_environment(); @@ -1150,11 +1267,9 @@ int main(int argc, char *argv[]) { r = parse_argv(argc, argv); if (r <= 0) - goto finish; - - r = bootctl_main(argc, argv); + return r; - finish: - free(arg_path); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return bootctl_main(argc, argv); } + +DEFINE_MAIN_FUNCTION(run); |