1088b4f5fSWarner Losh-- 24d846d26SWarner Losh-- SPDX-License-Identifier: BSD-2-Clause 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 3076c75816SKyle Evanslocal cli = require("cli") 31aedd6be5SKyle Evanslocal core = require("core") 32aedd6be5SKyle Evanslocal color = require("color") 33aedd6be5SKyle Evanslocal config = require("config") 34aedd6be5SKyle Evanslocal screen = require("screen") 35aedd6be5SKyle Evanslocal drawer = require("drawer") 36088b4f5fSWarner Losh 37aedd6be5SKyle Evanslocal menu = {} 38c8518398SKyle Evans 39beafe961SKyle Evanslocal drawn_menu 4043f7d9d1SKyle Evanslocal return_menu_entry = { 4143f7d9d1SKyle Evans entry_type = core.MENU_RETURN, 4243f7d9d1SKyle Evans name = "Back to main menu" .. color.highlight(" [Backspace]"), 4343f7d9d1SKyle Evans} 44ca16d83fSKyle Evans 4504af4229SKyle Evanslocal function OnOff(str, value) 4604af4229SKyle Evans if value then 478ce1744fSKyle Evans return str .. color.escapefg(color.GREEN) .. "On" .. 486dd078dfSToomas Soome color.resetfg() 49e15abd1fSKyle Evans else 508ce1744fSKyle Evans return str .. color.escapefg(color.RED) .. "off" .. 516dd078dfSToomas Soome color.resetfg() 52e15abd1fSKyle Evans end 53e15abd1fSKyle Evansend 54e15abd1fSKyle Evans 559ed6f9efSKyle Evanslocal function bootenvSet(env) 567efc058fSKyle Evans loader.setenv("vfs.root.mountfrom", env) 577efc058fSKyle Evans loader.setenv("currdev", env .. ":") 587efc058fSKyle Evans config.reload() 59d7584aa0SCyrus Rahman if loader.getenv("kernelname") ~= nil then 60d7584aa0SCyrus Rahman loader.perform("unload") 61d7584aa0SCyrus Rahman end 627efc058fSKyle Evansend 637efc058fSKyle Evans 649b17aa27SJessica Clarkelocal function multiUserPrompt() 659b17aa27SJessica Clarke return loader.getenv("loader_menu_multi_user_prompt") or "Multi user" 669b17aa27SJessica Clarkeend 679b17aa27SJessica Clarke 68b5746545SKyle Evans-- Module exports 698d415029SKyle Evansmenu.handlers = { 708d415029SKyle Evans -- Menu handlers take the current menu and selected entry as parameters, 718d415029SKyle Evans -- and should return a boolean indicating whether execution should 728d415029SKyle Evans -- continue or not. The return value may be omitted if this entry should 738d415029SKyle Evans -- have no bearing on whether we continue or not, indicating that we 748d415029SKyle Evans -- should just continue after execution. 75e2df27e3SKyle Evans [core.MENU_ENTRY] = function(_, entry) 768d415029SKyle Evans -- run function 77aedd6be5SKyle Evans entry.func() 788d415029SKyle Evans end, 79e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 808d415029SKyle Evans -- carousel (rotating) functionality 81aedd6be5SKyle Evans local carid = entry.carousel_id 82aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 834f437f9eSKyle Evans local choices = entry.items 844f437f9eSKyle Evans if type(choices) == "function" then 854f437f9eSKyle Evans choices = choices() 864f437f9eSKyle Evans end 879f71d421SKyle Evans if #choices > 0 then 88aedd6be5SKyle Evans caridx = (caridx % #choices) + 1 89aedd6be5SKyle Evans config.setCarouselIndex(carid, caridx) 90aedd6be5SKyle Evans entry.func(caridx, choices[caridx], choices) 918d415029SKyle Evans end 928d415029SKyle Evans end, 93e2df27e3SKyle Evans [core.MENU_SUBMENU] = function(_, entry) 94da9ab827SKyle Evans menu.process(entry.submenu) 958d415029SKyle Evans end, 96e2df27e3SKyle Evans [core.MENU_RETURN] = function(_, entry) 978d415029SKyle Evans -- allow entry to have a function/side effect 989f71d421SKyle Evans if entry.func ~= nil then 99aedd6be5SKyle Evans entry.func() 1008d415029SKyle Evans end 101aedd6be5SKyle Evans return false 1028d415029SKyle Evans end, 103aedd6be5SKyle Evans} 104280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome 105088b4f5fSWarner Losh 1067efc058fSKyle Evansmenu.boot_environments = { 1077efc058fSKyle Evans entries = { 1087efc058fSKyle Evans -- return to welcome menu 10943f7d9d1SKyle Evans return_menu_entry, 1107efc058fSKyle Evans { 1117efc058fSKyle Evans entry_type = core.MENU_CAROUSEL_ENTRY, 1127efc058fSKyle Evans carousel_id = "be_active", 1137efc058fSKyle Evans items = core.bootenvList, 1147efc058fSKyle Evans name = function(idx, choice, all_choices) 1157efc058fSKyle Evans if #all_choices == 0 then 1167efc058fSKyle Evans return "Active: " 1177efc058fSKyle Evans end 1187efc058fSKyle Evans 1197efc058fSKyle Evans local is_default = (idx == 1) 1207efc058fSKyle Evans local bootenv_name = "" 1217efc058fSKyle Evans local name_color 1227efc058fSKyle Evans if is_default then 1238ce1744fSKyle Evans name_color = color.escapefg(color.GREEN) 1247efc058fSKyle Evans else 125*baa7e9cdSWarner Losh name_color = color.escapefg(color.CYAN) 1267efc058fSKyle Evans end 1277efc058fSKyle Evans bootenv_name = bootenv_name .. name_color .. 1288ce1744fSKyle Evans choice .. color.resetfg() 1297efc058fSKyle Evans return color.highlight("A").."ctive: " .. 1307efc058fSKyle Evans bootenv_name .. " (" .. idx .. " of " .. 1317efc058fSKyle Evans #all_choices .. ")" 1327efc058fSKyle Evans end, 133e2df27e3SKyle Evans func = function(_, choice, _) 1347efc058fSKyle Evans bootenvSet(choice) 1357efc058fSKyle Evans end, 1367efc058fSKyle Evans alias = {"a", "A"}, 1377efc058fSKyle Evans }, 1387efc058fSKyle Evans { 1397efc058fSKyle Evans entry_type = core.MENU_ENTRY, 140277f38abSMariusz Zaborski visible = function() 141277f38abSMariusz Zaborski return core.isRewinded() == false 142277f38abSMariusz Zaborski end, 1437efc058fSKyle Evans name = function() 1447efc058fSKyle Evans return color.highlight("b") .. "ootfs: " .. 1457efc058fSKyle Evans core.bootenvDefault() 1467efc058fSKyle Evans end, 1477efc058fSKyle Evans func = function() 1487efc058fSKyle Evans -- Reset active boot environment to the default 1497efc058fSKyle Evans config.setCarouselIndex("be_active", 1) 1507efc058fSKyle Evans bootenvSet(core.bootenvDefault()) 1517efc058fSKyle Evans end, 1527efc058fSKyle Evans alias = {"b", "B"}, 1537efc058fSKyle Evans }, 1547efc058fSKyle Evans }, 1557efc058fSKyle Evans} 1567efc058fSKyle Evans 157088b4f5fSWarner Loshmenu.boot_options = { 158d8757746SKyle Evans entries = { 159088b4f5fSWarner Losh -- return to welcome menu 16043f7d9d1SKyle Evans return_menu_entry, 161088b4f5fSWarner Losh -- load defaults 162088b4f5fSWarner Losh { 163a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 164a51f9f0cSKyle Evans name = "Load System " .. color.highlight("D") .. 165a51f9f0cSKyle Evans "efaults", 166a51f9f0cSKyle Evans func = core.setDefaults, 1673cd5547bSKyle Evans alias = {"d", "D"}, 168088b4f5fSWarner Losh }, 169088b4f5fSWarner Losh { 170a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 171088b4f5fSWarner Losh }, 172088b4f5fSWarner Losh { 173a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 174a51f9f0cSKyle Evans name = "Boot Options:", 175088b4f5fSWarner Losh }, 176088b4f5fSWarner Losh -- acpi 177088b4f5fSWarner Losh { 178a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 179e0f3dc82SR. Christian McDonald visible = core.hasACPI, 180088b4f5fSWarner Losh name = function() 181fd2b19b3SKyle Evans return OnOff(color.highlight("A") .. 182aedd6be5SKyle Evans "CPI :", core.acpi) 183088b4f5fSWarner Losh end, 184a51f9f0cSKyle Evans func = core.setACPI, 1853cd5547bSKyle Evans alias = {"a", "A"}, 186088b4f5fSWarner Losh }, 187088b4f5fSWarner Losh -- safe mode 188088b4f5fSWarner Losh { 189a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 190088b4f5fSWarner Losh name = function() 19157099121SKyle Evans return OnOff("Safe " .. color.highlight("M") .. 192aedd6be5SKyle Evans "ode :", core.sm) 193088b4f5fSWarner Losh end, 194a51f9f0cSKyle Evans func = core.setSafeMode, 1953cd5547bSKyle Evans alias = {"m", "M"}, 196088b4f5fSWarner Losh }, 197088b4f5fSWarner Losh -- single user 198088b4f5fSWarner Losh { 199a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 200088b4f5fSWarner Losh name = function() 201fd2b19b3SKyle Evans return OnOff(color.highlight("S") .. 202aedd6be5SKyle Evans "ingle user:", core.su) 203088b4f5fSWarner Losh end, 204a51f9f0cSKyle Evans func = core.setSingleUser, 2053cd5547bSKyle Evans alias = {"s", "S"}, 206088b4f5fSWarner Losh }, 207088b4f5fSWarner Losh -- verbose boot 208088b4f5fSWarner Losh { 209a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 210088b4f5fSWarner Losh name = function() 211fd2b19b3SKyle Evans return OnOff(color.highlight("V") .. 212aedd6be5SKyle Evans "erbose :", core.verbose) 213088b4f5fSWarner Losh end, 214a51f9f0cSKyle Evans func = core.setVerbose, 2153cd5547bSKyle Evans alias = {"v", "V"}, 216088b4f5fSWarner Losh }, 217d8757746SKyle Evans }, 218aedd6be5SKyle Evans} 219088b4f5fSWarner Losh 220088b4f5fSWarner Loshmenu.welcome = { 221303253e5SKyle Evans entries = function() 222aedd6be5SKyle Evans local menu_entries = menu.welcome.all_entries 223d2187b39SRyan Moeller local multi_user = menu_entries.multi_user 224d2187b39SRyan Moeller local single_user = menu_entries.single_user 225d2187b39SRyan Moeller local boot_entry_1, boot_entry_2 2269f71d421SKyle Evans if core.isSingleUserBoot() then 227d2187b39SRyan Moeller -- Swap the first two menu items on single user boot. 228d2187b39SRyan Moeller -- We'll cache the alternate entries for performance. 229d2187b39SRyan Moeller local alts = menu_entries.alts 230d2187b39SRyan Moeller if alts == nil then 231d2187b39SRyan Moeller single_user = core.deepCopyTable(single_user) 232d2187b39SRyan Moeller multi_user = core.deepCopyTable(multi_user) 233d2187b39SRyan Moeller single_user.name = single_user.alternate_name 234d2187b39SRyan Moeller multi_user.name = multi_user.alternate_name 235d2187b39SRyan Moeller menu_entries.alts = { 236d2187b39SRyan Moeller single_user = single_user, 237d2187b39SRyan Moeller multi_user = multi_user, 238d2187b39SRyan Moeller } 239d2187b39SRyan Moeller else 240d2187b39SRyan Moeller single_user = alts.single_user 241d2187b39SRyan Moeller multi_user = alts.multi_user 2429a0904b0SKyle Evans end 243d2187b39SRyan Moeller boot_entry_1, boot_entry_2 = single_user, multi_user 244d2187b39SRyan Moeller else 245d2187b39SRyan Moeller boot_entry_1, boot_entry_2 = multi_user, single_user 246303253e5SKyle Evans end 247d2187b39SRyan Moeller return { 248d2187b39SRyan Moeller boot_entry_1, 249d2187b39SRyan Moeller boot_entry_2, 250d2187b39SRyan Moeller menu_entries.prompt, 251d2187b39SRyan Moeller menu_entries.reboot, 2528f3b3610SWarner Losh menu_entries.console, 253d2187b39SRyan Moeller { 254d2187b39SRyan Moeller entry_type = core.MENU_SEPARATOR, 255d2187b39SRyan Moeller }, 256d2187b39SRyan Moeller { 257d2187b39SRyan Moeller entry_type = core.MENU_SEPARATOR, 258d2187b39SRyan Moeller name = "Options:", 259d2187b39SRyan Moeller }, 260d2187b39SRyan Moeller menu_entries.kernel_options, 261d2187b39SRyan Moeller menu_entries.boot_options, 262277f38abSMariusz Zaborski menu_entries.zpool_checkpoints, 263d2187b39SRyan Moeller menu_entries.boot_envs, 264d2187b39SRyan Moeller menu_entries.chainload, 265e7ccd5b4SWarner Losh menu_entries.vendor, 2667cb65be9SWarner Losh { 2677cb65be9SWarner Losh entry_type = core.MENU_SEPARATOR, 2687cb65be9SWarner Losh }, 2697cb65be9SWarner Losh menu_entries.loader_needs_upgrade, 270d2187b39SRyan Moeller } 271303253e5SKyle Evans end, 272303253e5SKyle Evans all_entries = { 273d2187b39SRyan Moeller multi_user = { 274a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 2759b17aa27SJessica Clarke name = function() 2769b17aa27SJessica Clarke return color.highlight("B") .. "oot " .. 2779b17aa27SJessica Clarke multiUserPrompt() .. " " .. 2789b17aa27SJessica Clarke color.highlight("[Enter]") 2799b17aa27SJessica Clarke end, 2805c1b5165SKyle Evans -- Not a standard menu entry function! 2819b17aa27SJessica Clarke alternate_name = function() 2829b17aa27SJessica Clarke return color.highlight("B") .. "oot " .. 2839b17aa27SJessica Clarke multiUserPrompt() 2849b17aa27SJessica Clarke end, 285088b4f5fSWarner Losh func = function() 286aedd6be5SKyle Evans core.setSingleUser(false) 287aedd6be5SKyle Evans core.boot() 288088b4f5fSWarner Losh end, 2893cd5547bSKyle Evans alias = {"b", "B"}, 290088b4f5fSWarner Losh }, 291d2187b39SRyan Moeller single_user = { 292a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 293a51f9f0cSKyle Evans name = "Boot " .. color.highlight("S") .. "ingle user", 2945c1b5165SKyle Evans -- Not a standard menu entry function! 295a51f9f0cSKyle Evans alternate_name = "Boot " .. color.highlight("S") .. 296a51f9f0cSKyle Evans "ingle user " .. color.highlight("[Enter]"), 297088b4f5fSWarner Losh func = function() 298aedd6be5SKyle Evans core.setSingleUser(true) 299aedd6be5SKyle Evans core.boot() 300088b4f5fSWarner Losh end, 3013cd5547bSKyle Evans alias = {"s", "S"}, 302088b4f5fSWarner Losh }, 3038f3b3610SWarner Losh console = { 3048f3b3610SWarner Losh entry_type = core.MENU_ENTRY, 3058f3b3610SWarner Losh name = function() 3068f3b3610SWarner Losh return color.highlight("C") .. "ons: " .. core.getConsoleName() 3078f3b3610SWarner Losh end, 3088f3b3610SWarner Losh func = function() 3098f3b3610SWarner Losh core.nextConsoleChoice() 3108f3b3610SWarner Losh end, 3118f3b3610SWarner Losh alias = {"c", "C"}, 3128f3b3610SWarner Losh }, 313d2187b39SRyan Moeller prompt = { 314a7cf0562SKyle Evans entry_type = core.MENU_RETURN, 315a51f9f0cSKyle Evans name = color.highlight("Esc") .. "ape to loader prompt", 316ef625845SKyle Evans func = function() 317aedd6be5SKyle Evans loader.setenv("autoboot_delay", "NO") 318ef625845SKyle Evans end, 3193cd5547bSKyle Evans alias = {core.KEYSTR_ESCAPE}, 320088b4f5fSWarner Losh }, 321d2187b39SRyan Moeller reboot = { 322a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 323a51f9f0cSKyle Evans name = color.highlight("R") .. "eboot", 324088b4f5fSWarner Losh func = function() 325aedd6be5SKyle Evans loader.perform("reboot") 326088b4f5fSWarner Losh end, 3273cd5547bSKyle Evans alias = {"r", "R"}, 328088b4f5fSWarner Losh }, 329d2187b39SRyan Moeller kernel_options = { 330a7cf0562SKyle Evans entry_type = core.MENU_CAROUSEL_ENTRY, 331ada26c4aSKyle Evans carousel_id = "kernel", 332ada26c4aSKyle Evans items = core.kernelList, 333ada26c4aSKyle Evans name = function(idx, choice, all_choices) 3349f71d421SKyle Evans if #all_choices == 0 then 335aedd6be5SKyle Evans return "Kernel: " 336088b4f5fSWarner Losh end 337b1b1f2b8SKyle Evans 338aedd6be5SKyle Evans local is_default = (idx == 1) 339aedd6be5SKyle Evans local kernel_name = "" 340aedd6be5SKyle Evans local name_color 3419f71d421SKyle Evans if is_default then 3428ce1744fSKyle Evans name_color = color.escapefg(color.GREEN) 343aedd6be5SKyle Evans kernel_name = "default/" 344bcf48a15SKyle Evans else 345*baa7e9cdSWarner Losh name_color = color.escapefg(color.CYAN) 346b1b1f2b8SKyle Evans end 347fd2b19b3SKyle Evans kernel_name = kernel_name .. name_color .. 3488ce1744fSKyle Evans choice .. color.resetfg() 349fd2b19b3SKyle Evans return color.highlight("K") .. "ernel: " .. 350fd2b19b3SKyle Evans kernel_name .. " (" .. idx .. " of " .. 351aedd6be5SKyle Evans #all_choices .. ")" 352088b4f5fSWarner Losh end, 353e2df27e3SKyle Evans func = function(_, choice, _) 354e414851fSKyle Evans if loader.getenv("kernelname") ~= nil then 355e414851fSKyle Evans loader.perform("unload") 356e414851fSKyle Evans end 357322a2dddSKyle Evans config.selectKernel(choice) 358088b4f5fSWarner Losh end, 3593cd5547bSKyle Evans alias = {"k", "K"}, 360088b4f5fSWarner Losh }, 361d2187b39SRyan Moeller boot_options = { 362a7cf0562SKyle Evans entry_type = core.MENU_SUBMENU, 363a51f9f0cSKyle Evans name = "Boot " .. color.highlight("O") .. "ptions", 3649a28f948SKyle Evans submenu = menu.boot_options, 3653cd5547bSKyle Evans alias = {"o", "O"}, 366d8757746SKyle Evans }, 367277f38abSMariusz Zaborski zpool_checkpoints = { 368277f38abSMariusz Zaborski entry_type = core.MENU_ENTRY, 369277f38abSMariusz Zaborski name = function() 37094510c29SKyle Evans local rewind = "No" 371277f38abSMariusz Zaborski if core.isRewinded() then 372277f38abSMariusz Zaborski rewind = "Yes" 373277f38abSMariusz Zaborski end 374277f38abSMariusz Zaborski return "Rewind ZFS " .. color.highlight("C") .. 375277f38abSMariusz Zaborski "heckpoint: " .. rewind 376277f38abSMariusz Zaborski end, 377277f38abSMariusz Zaborski func = function() 378277f38abSMariusz Zaborski core.changeRewindCheckpoint() 379277f38abSMariusz Zaborski if core.isRewinded() then 380277f38abSMariusz Zaborski bootenvSet( 381277f38abSMariusz Zaborski core.bootenvDefaultRewinded()) 382277f38abSMariusz Zaborski else 383277f38abSMariusz Zaborski bootenvSet(core.bootenvDefault()) 384277f38abSMariusz Zaborski end 385277f38abSMariusz Zaborski config.setCarouselIndex("be_active", 1) 386277f38abSMariusz Zaborski end, 387277f38abSMariusz Zaborski visible = function() 388277f38abSMariusz Zaborski return core.isZFSBoot() and 389277f38abSMariusz Zaborski core.isCheckpointed() 390277f38abSMariusz Zaborski end, 391277f38abSMariusz Zaborski alias = {"c", "C"}, 392277f38abSMariusz Zaborski }, 393d2187b39SRyan Moeller boot_envs = { 3947efc058fSKyle Evans entry_type = core.MENU_SUBMENU, 3957efc058fSKyle Evans visible = function() 3967efc058fSKyle Evans return core.isZFSBoot() and 3977efc058fSKyle Evans #core.bootenvList() > 1 3987efc058fSKyle Evans end, 3997efc058fSKyle Evans name = "Boot " .. color.highlight("E") .. "nvironments", 4007efc058fSKyle Evans submenu = menu.boot_environments, 4017efc058fSKyle Evans alias = {"e", "E"}, 4027efc058fSKyle Evans }, 403d2187b39SRyan Moeller chainload = { 404bdf12807SKyle Evans entry_type = core.MENU_ENTRY, 405bdf12807SKyle Evans name = function() 406bdf12807SKyle Evans return 'Chain' .. color.highlight("L") .. 407bdf12807SKyle Evans "oad " .. loader.getenv('chain_disk') 408bdf12807SKyle Evans end, 409bdf12807SKyle Evans func = function() 410bdf12807SKyle Evans loader.perform("chain " .. 411bdf12807SKyle Evans loader.getenv('chain_disk')) 412bdf12807SKyle Evans end, 413bdf12807SKyle Evans visible = function() 414bdf12807SKyle Evans return loader.getenv('chain_disk') ~= nil 415bdf12807SKyle Evans end, 416bdf12807SKyle Evans alias = {"l", "L"}, 417bdf12807SKyle Evans }, 4180eac99f7SWarner Losh loader_needs_upgrade = { 4190eac99f7SWarner Losh entry_type = core.MENU_SEPARATOR, 4200eac99f7SWarner Losh name = function() 4217cb65be9SWarner Losh return color.highlight("Loader needs to be updated") 4227cb65be9SWarner Losh end, 4230eac99f7SWarner Losh visible = function() 4240eac99f7SWarner Losh return core.loaderTooOld() 4250eac99f7SWarner Losh end 4260eac99f7SWarner Losh }, 427e7ccd5b4SWarner Losh vendor = { 428e7ccd5b4SWarner Losh entry_type = core.MENU_ENTRY, 429556e66b7SWarner Losh visible = function() 430556e66b7SWarner Losh return false 431556e66b7SWarner Losh end 432e7ccd5b4SWarner Losh }, 433d8757746SKyle Evans }, 434aedd6be5SKyle Evans} 435088b4f5fSWarner Losh 43620a81676SKyle Evansmenu.default = menu.welcome 43728384160SKyle Evans-- current_alias_table will be used to keep our alias table consistent across 43828384160SKyle Evans-- screen redraws, instead of relying on whatever triggered the redraw to update 43928384160SKyle Evans-- the local alias_table in menu.process. 44028384160SKyle Evansmenu.current_alias_table = {} 44120a81676SKyle Evans 4422a11b810SKyle Evansfunction menu.draw(menudef) 443beafe961SKyle Evans -- Clear the screen, reset the cursor, then draw 444aedd6be5SKyle Evans screen.clear() 4452a11b810SKyle Evans menu.current_alias_table = drawer.drawscreen(menudef) 4462a11b810SKyle Evans drawn_menu = menudef 447decacd91SKyle Evans screen.defcursor() 44828384160SKyle Evansend 44928384160SKyle Evans 450ca16d83fSKyle Evans-- 'keypress' allows the caller to indicate that a key has been pressed that we 451ca16d83fSKyle Evans-- should process as our initial input. 4522a11b810SKyle Evansfunction menu.process(menudef, keypress) 4532a11b810SKyle Evans assert(menudef ~= nil) 45428384160SKyle Evans 4552a11b810SKyle Evans if drawn_menu ~= menudef then 4562a11b810SKyle Evans menu.draw(menudef) 4577dcffa90SKyle Evans end 458ca16d83fSKyle Evans 459da9ab827SKyle Evans while true do 460ca16d83fSKyle Evans local key = keypress or io.getchar() 461ca16d83fSKyle Evans keypress = nil 462088b4f5fSWarner Losh 463b458bf0dSKyle Evans -- Special key behaviors 4649f71d421SKyle Evans if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and 4652a11b810SKyle Evans menudef ~= menu.default then 466aedd6be5SKyle Evans break 4679f71d421SKyle Evans elseif key == core.KEY_ENTER then 468aedd6be5SKyle Evans core.boot() 469041929aaSKyle Evans -- Should not return. If it does, escape menu handling 470041929aaSKyle Evans -- and drop to loader prompt. 471041929aaSKyle Evans return false 472abc4f7e7SKyle Evans end 473abc4f7e7SKyle Evans 474abc4f7e7SKyle Evans key = string.char(key) 475088b4f5fSWarner Losh -- check to see if key is an alias 476aedd6be5SKyle Evans local sel_entry = nil 47728384160SKyle Evans for k, v in pairs(menu.current_alias_table) do 4789f71d421SKyle Evans if key == k then 479aedd6be5SKyle Evans sel_entry = v 48028384160SKyle Evans break 481088b4f5fSWarner Losh end 482088b4f5fSWarner Losh end 483088b4f5fSWarner Losh 484088b4f5fSWarner Losh -- if we have an alias do the assigned action: 4859f71d421SKyle Evans if sel_entry ~= nil then 486aedd6be5SKyle Evans local handler = menu.handlers[sel_entry.entry_type] 4872a11b810SKyle Evans assert(handler ~= nil) 488da9ab827SKyle Evans -- The handler's return value indicates if we 489da9ab827SKyle Evans -- need to exit this menu. An omitted or true 490da9ab827SKyle Evans -- return value means to continue. 4912a11b810SKyle Evans if handler(menudef, sel_entry) == false then 492da9ab827SKyle Evans return 493aefcaa7eSKyle Evans end 49428384160SKyle Evans -- If we got an alias key the screen is out of date... 49528384160SKyle Evans -- redraw it. 4962a11b810SKyle Evans menu.draw(menudef) 497088b4f5fSWarner Losh end 498088b4f5fSWarner Losh end 499088b4f5fSWarner Loshend 500088b4f5fSWarner Losh 501da9ab827SKyle Evansfunction menu.run() 502041929aaSKyle Evans local autoboot_key 503c84dbc53SKyle Evans local delay = loader.getenv("autoboot_delay") 504c84dbc53SKyle Evans 505c84dbc53SKyle Evans if delay ~= nil and delay:lower() == "no" then 506c84dbc53SKyle Evans delay = nil 507c84dbc53SKyle Evans else 508c84dbc53SKyle Evans delay = tonumber(delay) or 10 509c84dbc53SKyle Evans end 510c84dbc53SKyle Evans 511c84dbc53SKyle Evans if delay == -1 then 512c84dbc53SKyle Evans core.boot() 513c84dbc53SKyle Evans return 514c84dbc53SKyle Evans end 515c84dbc53SKyle Evans 516beafe961SKyle Evans menu.draw(menu.default) 517c84dbc53SKyle Evans 518041929aaSKyle Evans if delay ~= nil then 519041929aaSKyle Evans autoboot_key = menu.autoboot(delay) 520041929aaSKyle Evans 521041929aaSKyle Evans -- autoboot_key should return the key pressed. It will only 522041929aaSKyle Evans -- return nil if we hit the timeout and executed the timeout 523041929aaSKyle Evans -- command. Bail out. 524041929aaSKyle Evans if autoboot_key == nil then 525041929aaSKyle Evans return 526041929aaSKyle Evans end 527041929aaSKyle Evans end 528ca16d83fSKyle Evans 529ca16d83fSKyle Evans menu.process(menu.default, autoboot_key) 530beafe961SKyle Evans drawn_menu = nil 531da9ab827SKyle Evans 532da9ab827SKyle Evans screen.defcursor() 5333fb656f8SWarner Losh -- We explicitly want the newline print adds 534da9ab827SKyle Evans print("Exiting menu!") 535088b4f5fSWarner Loshend 536088b4f5fSWarner Losh 537c84dbc53SKyle Evansfunction menu.autoboot(delay) 5381495c98fSKyle Evans local x = loader.getenv("loader_menu_timeout_x") or 4 539101afbc6SWarner Losh local y = loader.getenv("loader_menu_timeout_y") or 24 540c84dbc53SKyle Evans local endtime = loader.time() + delay 541aedd6be5SKyle Evans local time 5423244729fSKyle Evans local last 543088b4f5fSWarner Losh repeat 544aedd6be5SKyle Evans time = endtime - loader.time() 5453244729fSKyle Evans if last == nil or last ~= time then 5463244729fSKyle Evans last = time 547aedd6be5SKyle Evans screen.setcursor(x, y) 5483fb656f8SWarner Losh printc("Autoboot in " .. time .. 549ed107537SKyle Evans " seconds. [Space] to pause ") 550aedd6be5SKyle Evans screen.defcursor() 5513244729fSKyle Evans end 5529f71d421SKyle Evans if io.ischar() then 553aedd6be5SKyle Evans local ch = io.getchar() 5549f71d421SKyle Evans if ch == core.KEY_ENTER then 555aedd6be5SKyle Evans break 556088b4f5fSWarner Losh else 5573fb656f8SWarner Losh -- Erase autoboot msg. While real VT100s 5583fb656f8SWarner Losh -- wouldn't scroll when receiving a char with 559604919c4SWarner Losh -- the cursor at (79, 24), bad emulators do. 5603fb656f8SWarner Losh -- Avoid the issue by stopping at 79. 561604919c4SWarner Losh screen.setcursor(1, y) 5623fb656f8SWarner Losh printc(string.rep(" ", 79)) 563aedd6be5SKyle Evans screen.defcursor() 56412b95c84SKyle Evans return ch 565088b4f5fSWarner Losh end 566088b4f5fSWarner Losh end 567088b4f5fSWarner Losh 568aedd6be5SKyle Evans loader.delay(50000) 569aedd6be5SKyle Evans until time <= 0 570088b4f5fSWarner Losh 571a76f8a5bSKyle Evans local cmd = loader.getenv("menu_timeout_command") or "boot" 572e9c3ceb1SKyle Evans cli_execute_unparsed(cmd) 573041929aaSKyle Evans return nil 574088b4f5fSWarner Loshend 575088b4f5fSWarner Losh 57676c75816SKyle Evans-- CLI commands 5778f7f3d08SKyle Evansfunction cli.menu() 57876c75816SKyle Evans menu.run() 57976c75816SKyle Evansend 58076c75816SKyle Evans 581aedd6be5SKyle Evansreturn menu 582