1088b4f5fSWarner Losh-- 272e39d71SKyle Evans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD 372e39d71SKyle Evans-- 4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 5e12ff891SKyle Evans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6088b4f5fSWarner Losh-- All rights reserved. 7088b4f5fSWarner Losh-- 8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without 9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions 10088b4f5fSWarner Losh-- are met: 11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright 12088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer. 13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright 14088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer in the 15088b4f5fSWarner Losh-- documentation and/or other materials provided with the distribution. 16088b4f5fSWarner Losh-- 17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20088b4f5fSWarner Losh-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27088b4f5fSWarner Losh-- SUCH DAMAGE. 28088b4f5fSWarner Losh-- 29088b4f5fSWarner Losh-- $FreeBSD$ 30088b4f5fSWarner Losh-- 31088b4f5fSWarner Losh 3276c75816SKyle Evanslocal cli = require("cli") 33aedd6be5SKyle Evanslocal core = require("core") 34aedd6be5SKyle Evanslocal color = require("color") 35aedd6be5SKyle Evanslocal config = require("config") 36aedd6be5SKyle Evanslocal screen = require("screen") 37aedd6be5SKyle Evanslocal drawer = require("drawer") 38088b4f5fSWarner Losh 39aedd6be5SKyle Evanslocal menu = {} 40c8518398SKyle Evans 41beafe961SKyle Evanslocal drawn_menu 4243f7d9d1SKyle Evanslocal return_menu_entry = { 4343f7d9d1SKyle Evans entry_type = core.MENU_RETURN, 4443f7d9d1SKyle Evans name = "Back to main menu" .. color.highlight(" [Backspace]"), 4543f7d9d1SKyle Evans} 46ca16d83fSKyle Evans 4704af4229SKyle Evanslocal function OnOff(str, value) 4804af4229SKyle Evans if value then 498ce1744fSKyle Evans return str .. color.escapefg(color.GREEN) .. "On" .. 506dd078dfSToomas Soome color.resetfg() 51e15abd1fSKyle Evans else 528ce1744fSKyle Evans return str .. color.escapefg(color.RED) .. "off" .. 536dd078dfSToomas Soome color.resetfg() 54e15abd1fSKyle Evans end 55e15abd1fSKyle Evansend 56e15abd1fSKyle Evans 579ed6f9efSKyle Evanslocal function bootenvSet(env) 587efc058fSKyle Evans loader.setenv("vfs.root.mountfrom", env) 597efc058fSKyle Evans loader.setenv("currdev", env .. ":") 607efc058fSKyle Evans config.reload() 617efc058fSKyle Evansend 627efc058fSKyle Evans 63b5746545SKyle Evans-- Module exports 648d415029SKyle Evansmenu.handlers = { 658d415029SKyle Evans -- Menu handlers take the current menu and selected entry as parameters, 668d415029SKyle Evans -- and should return a boolean indicating whether execution should 678d415029SKyle Evans -- continue or not. The return value may be omitted if this entry should 688d415029SKyle Evans -- have no bearing on whether we continue or not, indicating that we 698d415029SKyle Evans -- should just continue after execution. 70e2df27e3SKyle Evans [core.MENU_ENTRY] = function(_, entry) 718d415029SKyle Evans -- run function 72aedd6be5SKyle Evans entry.func() 738d415029SKyle Evans end, 74e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 758d415029SKyle Evans -- carousel (rotating) functionality 76aedd6be5SKyle Evans local carid = entry.carousel_id 77aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 784f437f9eSKyle Evans local choices = entry.items 794f437f9eSKyle Evans if type(choices) == "function" then 804f437f9eSKyle Evans choices = choices() 814f437f9eSKyle Evans end 829f71d421SKyle Evans if #choices > 0 then 83aedd6be5SKyle Evans caridx = (caridx % #choices) + 1 84aedd6be5SKyle Evans config.setCarouselIndex(carid, caridx) 85aedd6be5SKyle Evans entry.func(caridx, choices[caridx], choices) 868d415029SKyle Evans end 878d415029SKyle Evans end, 88e2df27e3SKyle Evans [core.MENU_SUBMENU] = function(_, entry) 89da9ab827SKyle Evans menu.process(entry.submenu) 908d415029SKyle Evans end, 91e2df27e3SKyle Evans [core.MENU_RETURN] = function(_, entry) 928d415029SKyle Evans -- allow entry to have a function/side effect 939f71d421SKyle Evans if entry.func ~= nil then 94aedd6be5SKyle Evans entry.func() 958d415029SKyle Evans end 96aedd6be5SKyle Evans return false 978d415029SKyle Evans end, 98aedd6be5SKyle Evans} 99280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome 100088b4f5fSWarner Losh 1017efc058fSKyle Evansmenu.boot_environments = { 1027efc058fSKyle Evans entries = { 1037efc058fSKyle Evans -- return to welcome menu 10443f7d9d1SKyle Evans return_menu_entry, 1057efc058fSKyle Evans { 1067efc058fSKyle Evans entry_type = core.MENU_CAROUSEL_ENTRY, 1077efc058fSKyle Evans carousel_id = "be_active", 1087efc058fSKyle Evans items = core.bootenvList, 1097efc058fSKyle Evans name = function(idx, choice, all_choices) 1107efc058fSKyle Evans if #all_choices == 0 then 1117efc058fSKyle Evans return "Active: " 1127efc058fSKyle Evans end 1137efc058fSKyle Evans 1147efc058fSKyle Evans local is_default = (idx == 1) 1157efc058fSKyle Evans local bootenv_name = "" 1167efc058fSKyle Evans local name_color 1177efc058fSKyle Evans if is_default then 1188ce1744fSKyle Evans name_color = color.escapefg(color.GREEN) 1197efc058fSKyle Evans else 1208ce1744fSKyle Evans name_color = color.escapefg(color.BLUE) 1217efc058fSKyle Evans end 1227efc058fSKyle Evans bootenv_name = bootenv_name .. name_color .. 1238ce1744fSKyle Evans choice .. color.resetfg() 1247efc058fSKyle Evans return color.highlight("A").."ctive: " .. 1257efc058fSKyle Evans bootenv_name .. " (" .. idx .. " of " .. 1267efc058fSKyle Evans #all_choices .. ")" 1277efc058fSKyle Evans end, 128e2df27e3SKyle Evans func = function(_, choice, _) 1297efc058fSKyle Evans bootenvSet(choice) 1307efc058fSKyle Evans end, 1317efc058fSKyle Evans alias = {"a", "A"}, 1327efc058fSKyle Evans }, 1337efc058fSKyle Evans { 1347efc058fSKyle Evans entry_type = core.MENU_ENTRY, 135277f38abSMariusz Zaborski visible = function() 136277f38abSMariusz Zaborski return core.isRewinded() == false 137277f38abSMariusz Zaborski end, 1387efc058fSKyle Evans name = function() 1397efc058fSKyle Evans return color.highlight("b") .. "ootfs: " .. 1407efc058fSKyle Evans core.bootenvDefault() 1417efc058fSKyle Evans end, 1427efc058fSKyle Evans func = function() 1437efc058fSKyle Evans -- Reset active boot environment to the default 1447efc058fSKyle Evans config.setCarouselIndex("be_active", 1) 1457efc058fSKyle Evans bootenvSet(core.bootenvDefault()) 1467efc058fSKyle Evans end, 1477efc058fSKyle Evans alias = {"b", "B"}, 1487efc058fSKyle Evans }, 1497efc058fSKyle Evans }, 1507efc058fSKyle Evans} 1517efc058fSKyle Evans 152088b4f5fSWarner Loshmenu.boot_options = { 153d8757746SKyle Evans entries = { 154088b4f5fSWarner Losh -- return to welcome menu 15543f7d9d1SKyle Evans return_menu_entry, 156088b4f5fSWarner Losh -- load defaults 157088b4f5fSWarner Losh { 158a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 159a51f9f0cSKyle Evans name = "Load System " .. color.highlight("D") .. 160a51f9f0cSKyle Evans "efaults", 161a51f9f0cSKyle Evans func = core.setDefaults, 1623cd5547bSKyle Evans alias = {"d", "D"}, 163088b4f5fSWarner Losh }, 164088b4f5fSWarner Losh { 165a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 166088b4f5fSWarner Losh }, 167088b4f5fSWarner Losh { 168a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 169a51f9f0cSKyle Evans name = "Boot Options:", 170088b4f5fSWarner Losh }, 171088b4f5fSWarner Losh -- acpi 172088b4f5fSWarner Losh { 173a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 174c1ab36f5SKyle Evans visible = core.isSystem386, 175088b4f5fSWarner Losh name = function() 176fd2b19b3SKyle Evans return OnOff(color.highlight("A") .. 177aedd6be5SKyle Evans "CPI :", core.acpi) 178088b4f5fSWarner Losh end, 179a51f9f0cSKyle Evans func = core.setACPI, 1803cd5547bSKyle Evans alias = {"a", "A"}, 181088b4f5fSWarner Losh }, 182088b4f5fSWarner Losh -- safe mode 183088b4f5fSWarner Losh { 184a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 185088b4f5fSWarner Losh name = function() 18657099121SKyle Evans return OnOff("Safe " .. color.highlight("M") .. 187aedd6be5SKyle Evans "ode :", core.sm) 188088b4f5fSWarner Losh end, 189a51f9f0cSKyle Evans func = core.setSafeMode, 1903cd5547bSKyle Evans alias = {"m", "M"}, 191088b4f5fSWarner Losh }, 192088b4f5fSWarner Losh -- single user 193088b4f5fSWarner Losh { 194a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 195088b4f5fSWarner Losh name = function() 196fd2b19b3SKyle Evans return OnOff(color.highlight("S") .. 197aedd6be5SKyle Evans "ingle user:", core.su) 198088b4f5fSWarner Losh end, 199a51f9f0cSKyle Evans func = core.setSingleUser, 2003cd5547bSKyle Evans alias = {"s", "S"}, 201088b4f5fSWarner Losh }, 202088b4f5fSWarner Losh -- verbose boot 203088b4f5fSWarner Losh { 204a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 205088b4f5fSWarner Losh name = function() 206fd2b19b3SKyle Evans return OnOff(color.highlight("V") .. 207aedd6be5SKyle Evans "erbose :", core.verbose) 208088b4f5fSWarner Losh end, 209a51f9f0cSKyle Evans func = core.setVerbose, 2103cd5547bSKyle Evans alias = {"v", "V"}, 211088b4f5fSWarner Losh }, 212d8757746SKyle Evans }, 213aedd6be5SKyle Evans} 214088b4f5fSWarner Losh 215088b4f5fSWarner Loshmenu.welcome = { 216303253e5SKyle Evans entries = function() 217aedd6be5SKyle Evans local menu_entries = menu.welcome.all_entries 218d2187b39SRyan Moeller local multi_user = menu_entries.multi_user 219d2187b39SRyan Moeller local single_user = menu_entries.single_user 220d2187b39SRyan Moeller local boot_entry_1, boot_entry_2 2219f71d421SKyle Evans if core.isSingleUserBoot() then 222d2187b39SRyan Moeller -- Swap the first two menu items on single user boot. 223d2187b39SRyan Moeller -- We'll cache the alternate entries for performance. 224d2187b39SRyan Moeller local alts = menu_entries.alts 225d2187b39SRyan Moeller if alts == nil then 226d2187b39SRyan Moeller single_user = core.deepCopyTable(single_user) 227d2187b39SRyan Moeller multi_user = core.deepCopyTable(multi_user) 228d2187b39SRyan Moeller single_user.name = single_user.alternate_name 229d2187b39SRyan Moeller multi_user.name = multi_user.alternate_name 230d2187b39SRyan Moeller menu_entries.alts = { 231d2187b39SRyan Moeller single_user = single_user, 232d2187b39SRyan Moeller multi_user = multi_user, 233d2187b39SRyan Moeller } 234d2187b39SRyan Moeller else 235d2187b39SRyan Moeller single_user = alts.single_user 236d2187b39SRyan Moeller multi_user = alts.multi_user 2379a0904b0SKyle Evans end 238d2187b39SRyan Moeller boot_entry_1, boot_entry_2 = single_user, multi_user 239d2187b39SRyan Moeller else 240d2187b39SRyan Moeller boot_entry_1, boot_entry_2 = multi_user, single_user 241303253e5SKyle Evans end 242d2187b39SRyan Moeller return { 243d2187b39SRyan Moeller boot_entry_1, 244d2187b39SRyan Moeller boot_entry_2, 245d2187b39SRyan Moeller menu_entries.prompt, 246d2187b39SRyan Moeller menu_entries.reboot, 2478f3b3610SWarner Losh menu_entries.console, 248d2187b39SRyan Moeller { 249d2187b39SRyan Moeller entry_type = core.MENU_SEPARATOR, 250d2187b39SRyan Moeller }, 251d2187b39SRyan Moeller { 252d2187b39SRyan Moeller entry_type = core.MENU_SEPARATOR, 253d2187b39SRyan Moeller name = "Options:", 254d2187b39SRyan Moeller }, 255d2187b39SRyan Moeller menu_entries.kernel_options, 256d2187b39SRyan Moeller menu_entries.boot_options, 257277f38abSMariusz Zaborski menu_entries.zpool_checkpoints, 258d2187b39SRyan Moeller menu_entries.boot_envs, 259d2187b39SRyan Moeller menu_entries.chainload, 260d2187b39SRyan Moeller } 261303253e5SKyle Evans end, 262303253e5SKyle Evans all_entries = { 263d2187b39SRyan Moeller multi_user = { 264a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 265a51f9f0cSKyle Evans name = color.highlight("B") .. "oot Multi user " .. 266a51f9f0cSKyle Evans color.highlight("[Enter]"), 2675c1b5165SKyle Evans -- Not a standard menu entry function! 268a51f9f0cSKyle Evans alternate_name = color.highlight("B") .. 269a51f9f0cSKyle Evans "oot Multi user", 270088b4f5fSWarner Losh func = function() 271aedd6be5SKyle Evans core.setSingleUser(false) 272aedd6be5SKyle Evans core.boot() 273088b4f5fSWarner Losh end, 2743cd5547bSKyle Evans alias = {"b", "B"}, 275088b4f5fSWarner Losh }, 276d2187b39SRyan Moeller single_user = { 277a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 278a51f9f0cSKyle Evans name = "Boot " .. color.highlight("S") .. "ingle user", 2795c1b5165SKyle Evans -- Not a standard menu entry function! 280a51f9f0cSKyle Evans alternate_name = "Boot " .. color.highlight("S") .. 281a51f9f0cSKyle Evans "ingle user " .. color.highlight("[Enter]"), 282088b4f5fSWarner Losh func = function() 283aedd6be5SKyle Evans core.setSingleUser(true) 284aedd6be5SKyle Evans core.boot() 285088b4f5fSWarner Losh end, 2863cd5547bSKyle Evans alias = {"s", "S"}, 287088b4f5fSWarner Losh }, 2888f3b3610SWarner Losh console = { 2898f3b3610SWarner Losh entry_type = core.MENU_ENTRY, 2908f3b3610SWarner Losh name = function() 2918f3b3610SWarner Losh return color.highlight("C") .. "ons: " .. core.getConsoleName() 2928f3b3610SWarner Losh end, 2938f3b3610SWarner Losh func = function() 2948f3b3610SWarner Losh core.nextConsoleChoice() 2958f3b3610SWarner Losh end, 2968f3b3610SWarner Losh alias = {"c", "C"}, 2978f3b3610SWarner Losh }, 298d2187b39SRyan Moeller prompt = { 299a7cf0562SKyle Evans entry_type = core.MENU_RETURN, 300a51f9f0cSKyle Evans name = color.highlight("Esc") .. "ape to loader prompt", 301ef625845SKyle Evans func = function() 302aedd6be5SKyle Evans loader.setenv("autoboot_delay", "NO") 303ef625845SKyle Evans end, 3043cd5547bSKyle Evans alias = {core.KEYSTR_ESCAPE}, 305088b4f5fSWarner Losh }, 306d2187b39SRyan Moeller reboot = { 307a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 308a51f9f0cSKyle Evans name = color.highlight("R") .. "eboot", 309088b4f5fSWarner Losh func = function() 310aedd6be5SKyle Evans loader.perform("reboot") 311088b4f5fSWarner Losh end, 3123cd5547bSKyle Evans alias = {"r", "R"}, 313088b4f5fSWarner Losh }, 314d2187b39SRyan Moeller kernel_options = { 315a7cf0562SKyle Evans entry_type = core.MENU_CAROUSEL_ENTRY, 316ada26c4aSKyle Evans carousel_id = "kernel", 317ada26c4aSKyle Evans items = core.kernelList, 318ada26c4aSKyle Evans name = function(idx, choice, all_choices) 3199f71d421SKyle Evans if #all_choices == 0 then 320aedd6be5SKyle Evans return "Kernel: " 321088b4f5fSWarner Losh end 322b1b1f2b8SKyle Evans 323aedd6be5SKyle Evans local is_default = (idx == 1) 324aedd6be5SKyle Evans local kernel_name = "" 325aedd6be5SKyle Evans local name_color 3269f71d421SKyle Evans if is_default then 3278ce1744fSKyle Evans name_color = color.escapefg(color.GREEN) 328aedd6be5SKyle Evans kernel_name = "default/" 329bcf48a15SKyle Evans else 3308ce1744fSKyle Evans name_color = color.escapefg(color.BLUE) 331b1b1f2b8SKyle Evans end 332fd2b19b3SKyle Evans kernel_name = kernel_name .. name_color .. 3338ce1744fSKyle Evans choice .. color.resetfg() 334fd2b19b3SKyle Evans return color.highlight("K") .. "ernel: " .. 335fd2b19b3SKyle Evans kernel_name .. " (" .. idx .. " of " .. 336aedd6be5SKyle Evans #all_choices .. ")" 337088b4f5fSWarner Losh end, 338e2df27e3SKyle Evans func = function(_, choice, _) 339e414851fSKyle Evans if loader.getenv("kernelname") ~= nil then 340e414851fSKyle Evans loader.perform("unload") 341e414851fSKyle Evans end 342322a2dddSKyle Evans config.selectKernel(choice) 343088b4f5fSWarner Losh end, 3443cd5547bSKyle Evans alias = {"k", "K"}, 345088b4f5fSWarner Losh }, 346d2187b39SRyan Moeller boot_options = { 347a7cf0562SKyle Evans entry_type = core.MENU_SUBMENU, 348a51f9f0cSKyle Evans name = "Boot " .. color.highlight("O") .. "ptions", 3499a28f948SKyle Evans submenu = menu.boot_options, 3503cd5547bSKyle Evans alias = {"o", "O"}, 351d8757746SKyle Evans }, 352277f38abSMariusz Zaborski zpool_checkpoints = { 353277f38abSMariusz Zaborski entry_type = core.MENU_ENTRY, 354277f38abSMariusz Zaborski name = function() 355*94510c29SKyle Evans local rewind = "No" 356277f38abSMariusz Zaborski if core.isRewinded() then 357277f38abSMariusz Zaborski rewind = "Yes" 358277f38abSMariusz Zaborski end 359277f38abSMariusz Zaborski return "Rewind ZFS " .. color.highlight("C") .. 360277f38abSMariusz Zaborski "heckpoint: " .. rewind 361277f38abSMariusz Zaborski end, 362277f38abSMariusz Zaborski func = function() 363277f38abSMariusz Zaborski core.changeRewindCheckpoint() 364277f38abSMariusz Zaborski if core.isRewinded() then 365277f38abSMariusz Zaborski bootenvSet( 366277f38abSMariusz Zaborski core.bootenvDefaultRewinded()) 367277f38abSMariusz Zaborski else 368277f38abSMariusz Zaborski bootenvSet(core.bootenvDefault()) 369277f38abSMariusz Zaborski end 370277f38abSMariusz Zaborski config.setCarouselIndex("be_active", 1) 371277f38abSMariusz Zaborski end, 372277f38abSMariusz Zaborski visible = function() 373277f38abSMariusz Zaborski return core.isZFSBoot() and 374277f38abSMariusz Zaborski core.isCheckpointed() 375277f38abSMariusz Zaborski end, 376277f38abSMariusz Zaborski alias = {"c", "C"}, 377277f38abSMariusz Zaborski }, 378d2187b39SRyan Moeller boot_envs = { 3797efc058fSKyle Evans entry_type = core.MENU_SUBMENU, 3807efc058fSKyle Evans visible = function() 3817efc058fSKyle Evans return core.isZFSBoot() and 3827efc058fSKyle Evans #core.bootenvList() > 1 3837efc058fSKyle Evans end, 3847efc058fSKyle Evans name = "Boot " .. color.highlight("E") .. "nvironments", 3857efc058fSKyle Evans submenu = menu.boot_environments, 3867efc058fSKyle Evans alias = {"e", "E"}, 3877efc058fSKyle Evans }, 388d2187b39SRyan Moeller chainload = { 389bdf12807SKyle Evans entry_type = core.MENU_ENTRY, 390bdf12807SKyle Evans name = function() 391bdf12807SKyle Evans return 'Chain' .. color.highlight("L") .. 392bdf12807SKyle Evans "oad " .. loader.getenv('chain_disk') 393bdf12807SKyle Evans end, 394bdf12807SKyle Evans func = function() 395bdf12807SKyle Evans loader.perform("chain " .. 396bdf12807SKyle Evans loader.getenv('chain_disk')) 397bdf12807SKyle Evans end, 398bdf12807SKyle Evans visible = function() 399bdf12807SKyle Evans return loader.getenv('chain_disk') ~= nil 400bdf12807SKyle Evans end, 401bdf12807SKyle Evans alias = {"l", "L"}, 402bdf12807SKyle Evans }, 403d8757746SKyle Evans }, 404aedd6be5SKyle Evans} 405088b4f5fSWarner Losh 40620a81676SKyle Evansmenu.default = menu.welcome 40728384160SKyle Evans-- current_alias_table will be used to keep our alias table consistent across 40828384160SKyle Evans-- screen redraws, instead of relying on whatever triggered the redraw to update 40928384160SKyle Evans-- the local alias_table in menu.process. 41028384160SKyle Evansmenu.current_alias_table = {} 41120a81676SKyle Evans 4122a11b810SKyle Evansfunction menu.draw(menudef) 413beafe961SKyle Evans -- Clear the screen, reset the cursor, then draw 414aedd6be5SKyle Evans screen.clear() 4152a11b810SKyle Evans menu.current_alias_table = drawer.drawscreen(menudef) 4162a11b810SKyle Evans drawn_menu = menudef 417decacd91SKyle Evans screen.defcursor() 41828384160SKyle Evansend 41928384160SKyle Evans 420ca16d83fSKyle Evans-- 'keypress' allows the caller to indicate that a key has been pressed that we 421ca16d83fSKyle Evans-- should process as our initial input. 4222a11b810SKyle Evansfunction menu.process(menudef, keypress) 4232a11b810SKyle Evans assert(menudef ~= nil) 42428384160SKyle Evans 4252a11b810SKyle Evans if drawn_menu ~= menudef then 4262a11b810SKyle Evans menu.draw(menudef) 4277dcffa90SKyle Evans end 428ca16d83fSKyle Evans 429da9ab827SKyle Evans while true do 430ca16d83fSKyle Evans local key = keypress or io.getchar() 431ca16d83fSKyle Evans keypress = nil 432088b4f5fSWarner Losh 433b458bf0dSKyle Evans -- Special key behaviors 4349f71d421SKyle Evans if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and 4352a11b810SKyle Evans menudef ~= menu.default then 436aedd6be5SKyle Evans break 4379f71d421SKyle Evans elseif key == core.KEY_ENTER then 438aedd6be5SKyle Evans core.boot() 439041929aaSKyle Evans -- Should not return. If it does, escape menu handling 440041929aaSKyle Evans -- and drop to loader prompt. 441041929aaSKyle Evans return false 442abc4f7e7SKyle Evans end 443abc4f7e7SKyle Evans 444abc4f7e7SKyle Evans key = string.char(key) 445088b4f5fSWarner Losh -- check to see if key is an alias 446aedd6be5SKyle Evans local sel_entry = nil 44728384160SKyle Evans for k, v in pairs(menu.current_alias_table) do 4489f71d421SKyle Evans if key == k then 449aedd6be5SKyle Evans sel_entry = v 45028384160SKyle Evans break 451088b4f5fSWarner Losh end 452088b4f5fSWarner Losh end 453088b4f5fSWarner Losh 454088b4f5fSWarner Losh -- if we have an alias do the assigned action: 4559f71d421SKyle Evans if sel_entry ~= nil then 456aedd6be5SKyle Evans local handler = menu.handlers[sel_entry.entry_type] 4572a11b810SKyle Evans assert(handler ~= nil) 458da9ab827SKyle Evans -- The handler's return value indicates if we 459da9ab827SKyle Evans -- need to exit this menu. An omitted or true 460da9ab827SKyle Evans -- return value means to continue. 4612a11b810SKyle Evans if handler(menudef, sel_entry) == false then 462da9ab827SKyle Evans return 463aefcaa7eSKyle Evans end 46428384160SKyle Evans -- If we got an alias key the screen is out of date... 46528384160SKyle Evans -- redraw it. 4662a11b810SKyle Evans menu.draw(menudef) 467088b4f5fSWarner Losh end 468088b4f5fSWarner Losh end 469088b4f5fSWarner Loshend 470088b4f5fSWarner Losh 471da9ab827SKyle Evansfunction menu.run() 472041929aaSKyle Evans local autoboot_key 473c84dbc53SKyle Evans local delay = loader.getenv("autoboot_delay") 474c84dbc53SKyle Evans 475c84dbc53SKyle Evans if delay ~= nil and delay:lower() == "no" then 476c84dbc53SKyle Evans delay = nil 477c84dbc53SKyle Evans else 478c84dbc53SKyle Evans delay = tonumber(delay) or 10 479c84dbc53SKyle Evans end 480c84dbc53SKyle Evans 481c84dbc53SKyle Evans if delay == -1 then 482c84dbc53SKyle Evans core.boot() 483c84dbc53SKyle Evans return 484c84dbc53SKyle Evans end 485c84dbc53SKyle Evans 486beafe961SKyle Evans menu.draw(menu.default) 487c84dbc53SKyle Evans 488041929aaSKyle Evans if delay ~= nil then 489041929aaSKyle Evans autoboot_key = menu.autoboot(delay) 490041929aaSKyle Evans 491041929aaSKyle Evans -- autoboot_key should return the key pressed. It will only 492041929aaSKyle Evans -- return nil if we hit the timeout and executed the timeout 493041929aaSKyle Evans -- command. Bail out. 494041929aaSKyle Evans if autoboot_key == nil then 495041929aaSKyle Evans return 496041929aaSKyle Evans end 497041929aaSKyle Evans end 498ca16d83fSKyle Evans 499ca16d83fSKyle Evans menu.process(menu.default, autoboot_key) 500beafe961SKyle Evans drawn_menu = nil 501da9ab827SKyle Evans 502da9ab827SKyle Evans screen.defcursor() 503da9ab827SKyle Evans print("Exiting menu!") 504088b4f5fSWarner Loshend 505088b4f5fSWarner Losh 506c84dbc53SKyle Evansfunction menu.autoboot(delay) 5071495c98fSKyle Evans local x = loader.getenv("loader_menu_timeout_x") or 4 5081495c98fSKyle Evans local y = loader.getenv("loader_menu_timeout_y") or 23 509c84dbc53SKyle Evans local endtime = loader.time() + delay 510aedd6be5SKyle Evans local time 5113244729fSKyle Evans local last 512088b4f5fSWarner Losh repeat 513aedd6be5SKyle Evans time = endtime - loader.time() 5143244729fSKyle Evans if last == nil or last ~= time then 5153244729fSKyle Evans last = time 516aedd6be5SKyle Evans screen.setcursor(x, y) 51757099121SKyle Evans print("Autoboot in " .. time .. 51857099121SKyle Evans " seconds, hit [Enter] to boot" .. 519aedd6be5SKyle Evans " or any other key to stop ") 520aedd6be5SKyle Evans screen.defcursor() 5213244729fSKyle Evans end 5229f71d421SKyle Evans if io.ischar() then 523aedd6be5SKyle Evans local ch = io.getchar() 5249f71d421SKyle Evans if ch == core.KEY_ENTER then 525aedd6be5SKyle Evans break 526088b4f5fSWarner Losh else 527088b4f5fSWarner Losh -- erase autoboot msg 528aedd6be5SKyle Evans screen.setcursor(0, y) 52966964bbcSKyle Evans print(string.rep(" ", 80)) 530aedd6be5SKyle Evans screen.defcursor() 53112b95c84SKyle Evans return ch 532088b4f5fSWarner Losh end 533088b4f5fSWarner Losh end 534088b4f5fSWarner Losh 535aedd6be5SKyle Evans loader.delay(50000) 536aedd6be5SKyle Evans until time <= 0 537088b4f5fSWarner Losh 538a76f8a5bSKyle Evans local cmd = loader.getenv("menu_timeout_command") or "boot" 539e9c3ceb1SKyle Evans cli_execute_unparsed(cmd) 540041929aaSKyle Evans return nil 541088b4f5fSWarner Loshend 542088b4f5fSWarner Losh 54376c75816SKyle Evans-- CLI commands 5448f7f3d08SKyle Evansfunction cli.menu() 54576c75816SKyle Evans menu.run() 54676c75816SKyle Evansend 54776c75816SKyle Evans 548aedd6be5SKyle Evansreturn menu 549