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