From 97af80c5a7029c3f92e982dcf9338b9e67ad9cde Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Oct 2018 20:43:45 +0200 Subject: [PATCH] systemctl: add support for booting into boot menu/entry (This also removes support for booting into the EFI firmware setup without logind. That's because otherwise the non-EFI fallback logind implements can't work.) Fixes: #9896 --- man/systemctl.xml | 29 ++++++-- src/systemctl/systemctl.c | 150 ++++++++++++++++++++++++++++++++++---- 2 files changed, 161 insertions(+), 18 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 08aacd8f41..512c255b39 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -617,11 +617,30 @@ - When used with the reboot command, - indicate to the system's firmware to boot into setup - mode. Note that this is currently only supported on some EFI - systems and only if the system was booted in EFI - mode. + When used with the reboot command, indicate to the system's firmware to reboot into + the firmware setup interface. Note that this functionality is not available on all systems. + + + + + + + + When used with the reboot command, indicate to the system's boot loader to show the + boot loader menu on the following boot. Takes a time value as parameter — indicating the menu time-out. Pass + zero in order to disable the menu time-out. Note that not all boot loaders support this + functionality. + + + + + + + + When used with the reboot command, indicate to the system's boot loader to boot into + a specific boot loader entry on the following boot. Takes a boot loader entry identifier as argument, or + help in order to list available entries. Note that not all boot loaders support this + functionality. diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9c67a69c23..38897ecefb 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -170,6 +170,8 @@ static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; static bool arg_firmware_setup = false; +static usec_t arg_boot_loader_menu = USEC_INFINITY; +static const char *arg_boot_loader_entry = NULL; static bool arg_now = false; static bool arg_jobs_before = false; static bool arg_jobs_after = false; @@ -3473,7 +3475,11 @@ static int logind_check_inhibitors(enum action a) { #endif } -static int logind_prepare_firmware_setup(void) { +static int prepare_firmware_setup(void) { + + if (!arg_firmware_setup) + return 0; + #if ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; @@ -3497,27 +3503,75 @@ static int logind_prepare_firmware_setup(void) { return 0; #else - log_error("Cannot remotely indicate to EFI to boot into setup mode."); + log_error("Booting into firmware setup not supported."); return -ENOSYS; #endif } -static int prepare_firmware_setup(void) { - int r; +static int prepare_boot_loader_menu(void) { - if (!arg_firmware_setup) + if (arg_boot_loader_menu == USEC_INFINITY) return 0; - if (arg_transport == BUS_TRANSPORT_LOCAL) { +#if ENABLE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; - r = efi_set_reboot_to_firmware(true); - if (r < 0) - log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m"); - else - return r; - } + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; - return logind_prepare_firmware_setup(); + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToBootLoaderMenu", + &error, + NULL, + "t", arg_boot_loader_menu); + if (r < 0) + return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r)); + + return 0; +#else + log_error("Booting into boot loader menu not supported."); + return -ENOSYS; +#endif +} + +static int prepare_boot_loader_entry(void) { + + if (!arg_boot_loader_entry) + return 0; + +#if ENABLE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToBootLoaderEntry", + &error, + NULL, + "s", arg_boot_loader_entry); + if (r < 0) + return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r)); + + return 0; +#else + log_error("Booting into boot loader entry not supported."); + return -ENOSYS; +#endif } static int load_kexec_kernel(void) { @@ -3644,6 +3698,14 @@ static int start_special(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = prepare_boot_loader_menu(); + if (r < 0) + return r; + + r = prepare_boot_loader_entry(); + if (r < 0) + return r; + if (a == ACTION_REBOOT && argc > 1) { r = update_reboot_parameter_and_warn(argv[1]); if (r < 0) @@ -7522,6 +7584,10 @@ static int systemctl_help(void) { " short-monotonic, short-unix,\n" " verbose, export, json, json-pretty, json-sse, cat)\n" " --firmware-setup Tell the firmware to show the setup menu on next boot\n" + " --boot-loader-menu=TIME\n" + " Boot into boot loader menu on next boot\n" + " --boot-loader-entry=NAME\n" + " Boot into a specific boot loader entry on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" "Unit Commands:\n" " list-units [PATTERN...] List units currently in memory\n" @@ -7776,6 +7842,39 @@ static void help_states(void) { DUMP_STRING_TABLE(timer_state, TimerState, _TIMER_STATE_MAX); } +static int help_boot_loader_entry(void) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char **l = NULL; + sd_bus *bus; + char **i; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "BootLoaderEntries", + &error, + &l); + if (r < 0) + return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r)); + + if (strv_isempty(l)) { + log_error("No boot loader entries discovered."); + return -ENODATA; + } + + STRV_FOREACH(i, l) + puts(*i); + + return 0; +} + static int systemctl_parse_argv(int argc, char *argv[]) { enum { ARG_FAIL = 0x100, @@ -7806,6 +7905,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_JOB_MODE, ARG_PRESET_MODE, ARG_FIRMWARE_SETUP, + ARG_BOOT_LOADER_MENU, + ARG_BOOT_LOADER_ENTRY, ARG_NOW, ARG_MESSAGE, ARG_WAIT, @@ -7855,6 +7956,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "recursive", no_argument, NULL, 'r' }, { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, + { "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU }, + { "boot-loader-entry", required_argument, NULL, ARG_BOOT_LOADER_ENTRY }, { "now", no_argument, NULL, ARG_NOW }, { "message", required_argument, NULL, ARG_MESSAGE }, {} @@ -8128,6 +8231,27 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_firmware_setup = true; break; + case ARG_BOOT_LOADER_MENU: + + r = parse_sec(optarg, &arg_boot_loader_menu); + if (r < 0) + return log_error_errno(r, "Failed to parse --boot-loader-menu= argument '%s': %m", optarg); + + break; + + case ARG_BOOT_LOADER_ENTRY: + + if (streq(optarg, "help")) { /* Yes, this means, "help" is not a valid boot loader entry name we can deal with */ + r = help_boot_loader_entry(); + if (r < 0) + return r; + + return 0; + } + + arg_boot_loader_entry = empty_to_null(optarg); + break; + case ARG_STATE: { if (isempty(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL),