Index: head/stand/lua/core.lua =================================================================== --- head/stand/lua/core.lua +++ head/stand/lua/core.lua @@ -242,6 +242,41 @@ return kernels end +function core.bootenvDefault() + return loader.getenv("zfs_be_active") +end + +function core.bootenvList() + local bootenv_count = tonumber(loader.getenv("bootenvs_count")) + local bootenvs = {} + local curenv + local curenv_idx = 0 + local envcount = 0 + local unique = {} + + if bootenv_count == nil or bootenv_count <= 0 then + return bootenvs + end + + -- Currently selected bootenv is always first/default + curenv = core.bootenvDefault() + if curenv ~= nil then + envcount = envcount + 1 + bootenvs[envcount] = curenv + unique[curenv] = true + end + + for curenv_idx = 0, bootenv_count - 1 do + curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") + if curenv ~= nil and unique[curenv] == nil then + envcount = envcount + 1 + bootenvs[envcount] = curenv + unique[curenv] = true + end + end + return bootenvs +end + function core.setDefaults() core.setACPI(core.getACPIPresent(true)) core.setSafeMode(false) @@ -262,6 +297,15 @@ function core.isSingleUserBoot() local single_user = loader.getenv("boot_single") return single_user ~= nil and single_user:lower() == "yes" +end + +function core.isZFSBoot() + local c = loader.getenv("currdev") + + if c ~= nil then + return c:match("^zfs:") ~= nil + end + return false end function core.isSerialBoot() Index: head/stand/lua/menu.lua =================================================================== --- head/stand/lua/menu.lua +++ head/stand/lua/menu.lua @@ -50,6 +50,12 @@ end end +local bootenvSet = function(env) + loader.setenv("vfs.root.mountfrom", env) + loader.setenv("currdev", env .. ":") + config.reload() +end + -- Module exports menu.handlers = { -- Menu handlers take the current menu and selected entry as parameters, @@ -89,6 +95,58 @@ } -- loader menu tree is rooted at menu.welcome +menu.boot_environments = { + entries = { + -- return to welcome menu + { + entry_type = core.MENU_RETURN, + name = "Back to main menu" .. + color.highlight(" [Backspace]"), + }, + { + entry_type = core.MENU_CAROUSEL_ENTRY, + carousel_id = "be_active", + items = core.bootenvList, + name = function(idx, choice, all_choices) + if #all_choices == 0 then + return "Active: " + end + + local is_default = (idx == 1) + local bootenv_name = "" + local name_color + if is_default then + name_color = color.escapef(color.GREEN) + else + name_color = color.escapef(color.BLUE) + end + bootenv_name = bootenv_name .. name_color .. + choice .. color.default() + return color.highlight("A").."ctive: " .. + bootenv_name .. " (" .. idx .. " of " .. + #all_choices .. ")" + end, + func = function(idx, choice, all_choices) + bootenvSet(choice) + end, + alias = {"a", "A"}, + }, + { + entry_type = core.MENU_ENTRY, + name = function() + return color.highlight("b") .. "ootfs: " .. + core.bootenvDefault() + end, + func = function() + -- Reset active boot environment to the default + config.setCarouselIndex("be_active", 1) + bootenvSet(core.bootenvDefault()) + end, + alias = {"b", "B"}, + }, + }, +} + menu.boot_options = { entries = { -- return to welcome menu @@ -269,6 +327,17 @@ name = "Boot " .. color.highlight("O") .. "ptions", submenu = menu.boot_options, alias = {"o", "O"} + }, + -- boot environments + { + entry_type = core.MENU_SUBMENU, + visible = function() + return core.isZFSBoot() and + #core.bootenvList() > 1 + end, + name = "Boot " .. color.highlight("E") .. "nvironments", + submenu = menu.boot_environments, + alias = {"e", "E"}, }, }, }