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
This commit is contained in:
Lennart Poettering 2018-10-22 20:43:45 +02:00
parent 31b221cf5e
commit 97af80c5a7
2 changed files with 161 additions and 18 deletions

View File

@ -617,11 +617,30 @@
<term><option>--firmware-setup</option></term>
<listitem>
<para>When used with the <command>reboot</command> 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.</para>
<para>When used with the <command>reboot</command> command, indicate to the system's firmware to reboot into
the firmware setup interface. Note that this functionality is not available on all systems.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--boot-loader-menu=</option></term>
<listitem>
<para>When used with the <command>reboot</command> 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.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--boot-loader-entry=</option></term>
<listitem>
<para>When used with the <command>reboot</command> 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
<literal>help</literal> in order to list available entries. Note that not all boot loaders support this
functionality.</para>
</listitem>
</varlistentry>

View File

@ -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),