1088b4f5fSWarner Losh-- 2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 321d5bcbeSKyle Evans-- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org> 4088b4f5fSWarner Losh-- All rights reserved. 5088b4f5fSWarner Losh-- 6088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without 7088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions 8088b4f5fSWarner Losh-- are met: 9088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright 10088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer. 11088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright 12088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer in the 13088b4f5fSWarner Losh-- documentation and/or other materials provided with the distribution. 14088b4f5fSWarner Losh-- 15088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18088b4f5fSWarner Losh-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25088b4f5fSWarner Losh-- SUCH DAMAGE. 26088b4f5fSWarner Losh-- 27088b4f5fSWarner Losh-- $FreeBSD$ 28088b4f5fSWarner Losh-- 29088b4f5fSWarner Losh 30088b4f5fSWarner Losh 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 39aedd6be5SKyle Evanslocal skip 40aedd6be5SKyle Evanslocal run 41aedd6be5SKyle Evanslocal autoboot 42088b4f5fSWarner Losh 43e15abd1fSKyle Evanslocal OnOff = function(str, b) 449f71d421SKyle Evans if b then 45e15abd1fSKyle Evans return str .. color.escapef(color.GREEN) .. "On" .. 46aedd6be5SKyle Evans color.escapef(color.WHITE) 47e15abd1fSKyle Evans else 48e15abd1fSKyle Evans return str .. color.escapef(color.RED) .. "off" .. 49aedd6be5SKyle Evans color.escapef(color.WHITE) 50e15abd1fSKyle Evans end 51e15abd1fSKyle Evansend 52e15abd1fSKyle Evans 53b5746545SKyle Evans-- Module exports 548d415029SKyle Evansmenu.handlers = { 558d415029SKyle Evans -- Menu handlers take the current menu and selected entry as parameters, 568d415029SKyle Evans -- and should return a boolean indicating whether execution should 578d415029SKyle Evans -- continue or not. The return value may be omitted if this entry should 588d415029SKyle Evans -- have no bearing on whether we continue or not, indicating that we 598d415029SKyle Evans -- should just continue after execution. 608d415029SKyle Evans [core.MENU_ENTRY] = function(current_menu, entry) 618d415029SKyle Evans -- run function 62aedd6be5SKyle Evans entry.func() 638d415029SKyle Evans end, 648d415029SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry) 658d415029SKyle Evans -- carousel (rotating) functionality 66aedd6be5SKyle Evans local carid = entry.carousel_id 67aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 68aedd6be5SKyle Evans local choices = entry.items() 698d415029SKyle Evans 709f71d421SKyle Evans if #choices > 0 then 71aedd6be5SKyle Evans caridx = (caridx % #choices) + 1 72aedd6be5SKyle Evans config.setCarouselIndex(carid, caridx) 73aedd6be5SKyle Evans entry.func(caridx, choices[caridx], choices) 748d415029SKyle Evans end 758d415029SKyle Evans end, 768d415029SKyle Evans [core.MENU_SUBMENU] = function(current_menu, entry) 778d415029SKyle Evans -- recurse 789a28f948SKyle Evans return menu.run(entry.submenu) 798d415029SKyle Evans end, 808d415029SKyle Evans [core.MENU_RETURN] = function(current_menu, entry) 818d415029SKyle Evans -- allow entry to have a function/side effect 829f71d421SKyle Evans if entry.func ~= nil then 83aedd6be5SKyle Evans entry.func() 848d415029SKyle Evans end 85aedd6be5SKyle Evans return false 868d415029SKyle Evans end, 87aedd6be5SKyle Evans} 88280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome 89088b4f5fSWarner Losh 90088b4f5fSWarner Loshmenu.boot_options = { 91d8757746SKyle Evans entries = { 92088b4f5fSWarner Losh -- return to welcome menu 93088b4f5fSWarner Losh { 94a7cf0562SKyle Evans entry_type = core.MENU_RETURN, 95*a51f9f0cSKyle Evans name = "Back to main menu" .. 96*a51f9f0cSKyle Evans color.highlight(" [Backspace]"), 97088b4f5fSWarner Losh }, 98088b4f5fSWarner Losh -- load defaults 99088b4f5fSWarner Losh { 100a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 101*a51f9f0cSKyle Evans name = "Load System " .. color.highlight("D") .. 102*a51f9f0cSKyle Evans "efaults", 103*a51f9f0cSKyle Evans func = core.setDefaults, 104088b4f5fSWarner Losh alias = {"d", "D"} 105088b4f5fSWarner Losh }, 106088b4f5fSWarner Losh { 107a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 108088b4f5fSWarner Losh }, 109088b4f5fSWarner Losh { 110a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 111*a51f9f0cSKyle Evans name = "Boot Options:", 112088b4f5fSWarner Losh }, 113088b4f5fSWarner Losh -- acpi 114088b4f5fSWarner Losh { 115a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 116c1ab36f5SKyle Evans visible = core.isSystem386, 117088b4f5fSWarner Losh name = function() 118fd2b19b3SKyle Evans return OnOff(color.highlight("A") .. 119aedd6be5SKyle Evans "CPI :", core.acpi) 120088b4f5fSWarner Losh end, 121*a51f9f0cSKyle Evans func = core.setACPI, 122088b4f5fSWarner Losh alias = {"a", "A"} 123088b4f5fSWarner Losh }, 124088b4f5fSWarner Losh -- safe mode 125088b4f5fSWarner Losh { 126a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 127088b4f5fSWarner Losh name = function() 12857099121SKyle Evans return OnOff("Safe " .. color.highlight("M") .. 129aedd6be5SKyle Evans "ode :", core.sm) 130088b4f5fSWarner Losh end, 131*a51f9f0cSKyle Evans func = core.setSafeMode, 132088b4f5fSWarner Losh alias = {"m", "M"} 133088b4f5fSWarner Losh }, 134088b4f5fSWarner Losh -- single user 135088b4f5fSWarner Losh { 136a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 137088b4f5fSWarner Losh name = function() 138fd2b19b3SKyle Evans return OnOff(color.highlight("S") .. 139aedd6be5SKyle Evans "ingle user:", core.su) 140088b4f5fSWarner Losh end, 141*a51f9f0cSKyle Evans func = core.setSingleUser, 142088b4f5fSWarner Losh alias = {"s", "S"} 143088b4f5fSWarner Losh }, 144088b4f5fSWarner Losh -- verbose boot 145088b4f5fSWarner Losh { 146a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 147088b4f5fSWarner Losh name = function() 148fd2b19b3SKyle Evans return OnOff(color.highlight("V") .. 149aedd6be5SKyle Evans "erbose :", core.verbose) 150088b4f5fSWarner Losh end, 151*a51f9f0cSKyle Evans func = core.setVerbose, 152088b4f5fSWarner Losh alias = {"v", "V"} 153088b4f5fSWarner Losh }, 154d8757746SKyle Evans }, 155aedd6be5SKyle Evans} 156088b4f5fSWarner Losh 157088b4f5fSWarner Loshmenu.welcome = { 158303253e5SKyle Evans entries = function() 159aedd6be5SKyle Evans local menu_entries = menu.welcome.all_entries 160303253e5SKyle Evans -- Swap the first two menu items on single user boot 1619f71d421SKyle Evans if core.isSingleUserBoot() then 1629a0904b0SKyle Evans -- We'll cache the swapped menu, for performance 1639f71d421SKyle Evans if menu.welcome.swapped_menu ~= nil then 164aedd6be5SKyle Evans return menu.welcome.swapped_menu 1659a0904b0SKyle Evans end 1665c1b5165SKyle Evans -- Shallow copy the table 167aedd6be5SKyle Evans menu_entries = core.shallowCopyTable(menu_entries) 1685c1b5165SKyle Evans 1699a0904b0SKyle Evans -- Swap the first two menu entries 1704b6da14cSKyle Evans menu_entries[1], menu_entries[2] = 171aedd6be5SKyle Evans menu_entries[2], menu_entries[1] 172303253e5SKyle Evans 1739a0904b0SKyle Evans -- Then set their names to their alternate names 1749a0904b0SKyle Evans menu_entries[1].name, menu_entries[2].name = 1759a0904b0SKyle Evans menu_entries[1].alternate_name, 176aedd6be5SKyle Evans menu_entries[2].alternate_name 177aedd6be5SKyle Evans menu.welcome.swapped_menu = menu_entries 178303253e5SKyle Evans end 179aedd6be5SKyle Evans return menu_entries 180303253e5SKyle Evans end, 181303253e5SKyle Evans all_entries = { 182088b4f5fSWarner Losh -- boot multi user 183088b4f5fSWarner Losh { 184a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 185*a51f9f0cSKyle Evans name = color.highlight("B") .. "oot Multi user " .. 186*a51f9f0cSKyle Evans color.highlight("[Enter]"), 1875c1b5165SKyle Evans -- Not a standard menu entry function! 188*a51f9f0cSKyle Evans alternate_name = color.highlight("B") .. 189*a51f9f0cSKyle Evans "oot Multi user", 190088b4f5fSWarner Losh func = function() 191aedd6be5SKyle Evans core.setSingleUser(false) 192aedd6be5SKyle Evans core.boot() 193088b4f5fSWarner Losh end, 194b458bf0dSKyle Evans alias = {"b", "B"} 195088b4f5fSWarner Losh }, 196088b4f5fSWarner Losh -- boot single user 197088b4f5fSWarner Losh { 198a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 199*a51f9f0cSKyle Evans name = "Boot " .. color.highlight("S") .. "ingle user", 2005c1b5165SKyle Evans -- Not a standard menu entry function! 201*a51f9f0cSKyle Evans alternate_name = "Boot " .. color.highlight("S") .. 202*a51f9f0cSKyle Evans "ingle user " .. color.highlight("[Enter]"), 203088b4f5fSWarner Losh func = function() 204aedd6be5SKyle Evans core.setSingleUser(true) 205aedd6be5SKyle Evans core.boot() 206088b4f5fSWarner Losh end, 207088b4f5fSWarner Losh alias = {"s", "S"} 208088b4f5fSWarner Losh }, 209088b4f5fSWarner Losh -- escape to interpreter 210088b4f5fSWarner Losh { 211a7cf0562SKyle Evans entry_type = core.MENU_RETURN, 212*a51f9f0cSKyle Evans name = color.highlight("Esc") .. "ape to loader prompt", 213ef625845SKyle Evans func = function() 214aedd6be5SKyle Evans loader.setenv("autoboot_delay", "NO") 215ef625845SKyle Evans end, 21639006570SKyle Evans alias = {core.KEYSTR_ESCAPE} 217088b4f5fSWarner Losh }, 218088b4f5fSWarner Losh -- reboot 219088b4f5fSWarner Losh { 220a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 221*a51f9f0cSKyle Evans name = color.highlight("R") .. "eboot", 222088b4f5fSWarner Losh func = function() 223aedd6be5SKyle Evans loader.perform("reboot") 224088b4f5fSWarner Losh end, 225088b4f5fSWarner Losh alias = {"r", "R"} 226088b4f5fSWarner Losh }, 227088b4f5fSWarner Losh { 228a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 229088b4f5fSWarner Losh }, 230088b4f5fSWarner Losh { 231a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 232*a51f9f0cSKyle Evans name = "Options:", 233088b4f5fSWarner Losh }, 234088b4f5fSWarner Losh -- kernel options 235088b4f5fSWarner Losh { 236a7cf0562SKyle Evans entry_type = core.MENU_CAROUSEL_ENTRY, 237ada26c4aSKyle Evans carousel_id = "kernel", 238ada26c4aSKyle Evans items = core.kernelList, 239ada26c4aSKyle Evans name = function(idx, choice, all_choices) 2409f71d421SKyle Evans if #all_choices == 0 then 241aedd6be5SKyle Evans return "Kernel: " 242088b4f5fSWarner Losh end 243b1b1f2b8SKyle Evans 244aedd6be5SKyle Evans local is_default = (idx == 1) 245aedd6be5SKyle Evans local kernel_name = "" 246aedd6be5SKyle Evans local name_color 2479f71d421SKyle Evans if is_default then 248aedd6be5SKyle Evans name_color = color.escapef(color.GREEN) 249aedd6be5SKyle Evans kernel_name = "default/" 250bcf48a15SKyle Evans else 251aedd6be5SKyle Evans name_color = color.escapef(color.BLUE) 252b1b1f2b8SKyle Evans end 253fd2b19b3SKyle Evans kernel_name = kernel_name .. name_color .. 254aedd6be5SKyle Evans choice .. color.default() 255fd2b19b3SKyle Evans return color.highlight("K") .. "ernel: " .. 256fd2b19b3SKyle Evans kernel_name .. " (" .. idx .. " of " .. 257aedd6be5SKyle Evans #all_choices .. ")" 258088b4f5fSWarner Losh end, 2595d1e2f83SKyle Evans func = function(idx, choice, all_choices) 260aedd6be5SKyle Evans config.selectkernel(choice) 261088b4f5fSWarner Losh end, 262088b4f5fSWarner Losh alias = {"k", "K"} 263088b4f5fSWarner Losh }, 264088b4f5fSWarner Losh -- boot options 265088b4f5fSWarner Losh { 266a7cf0562SKyle Evans entry_type = core.MENU_SUBMENU, 267*a51f9f0cSKyle Evans name = "Boot " .. color.highlight("O") .. "ptions", 2689a28f948SKyle Evans submenu = menu.boot_options, 269088b4f5fSWarner Losh alias = {"o", "O"} 270d8757746SKyle Evans }, 271d8757746SKyle Evans }, 272aedd6be5SKyle Evans} 273088b4f5fSWarner Losh 27420a81676SKyle Evansmenu.default = menu.welcome 27520a81676SKyle Evans 276088b4f5fSWarner Loshfunction menu.run(m) 277088b4f5fSWarner Losh 2789f71d421SKyle Evans if menu.skip() then 279aedd6be5SKyle Evans core.autoboot() 280aedd6be5SKyle Evans return false 281088b4f5fSWarner Losh end 282088b4f5fSWarner Losh 2839f71d421SKyle Evans if m == nil then 28420a81676SKyle Evans m = menu.default 285088b4f5fSWarner Losh end 286088b4f5fSWarner Losh 287088b4f5fSWarner Losh -- redraw screen 288aedd6be5SKyle Evans screen.clear() 289aedd6be5SKyle Evans screen.defcursor() 290aedd6be5SKyle Evans local alias_table = drawer.drawscreen(m) 291088b4f5fSWarner Losh 292aedd6be5SKyle Evans menu.autoboot() 293088b4f5fSWarner Losh 294aedd6be5SKyle Evans cont = true 2959f71d421SKyle Evans while cont do 296aedd6be5SKyle Evans local key = io.getchar() 297088b4f5fSWarner Losh 298b458bf0dSKyle Evans -- Special key behaviors 2999f71d421SKyle Evans if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and 30020a81676SKyle Evans m ~= menu.default then 301aedd6be5SKyle Evans break 3029f71d421SKyle Evans elseif key == core.KEY_ENTER then 303aedd6be5SKyle Evans core.boot() 304b458bf0dSKyle Evans -- Should not return 305abc4f7e7SKyle Evans end 306abc4f7e7SKyle Evans 307abc4f7e7SKyle Evans key = string.char(key) 308088b4f5fSWarner Losh -- check to see if key is an alias 309aedd6be5SKyle Evans local sel_entry = nil 310088b4f5fSWarner Losh for k, v in pairs(alias_table) do 3119f71d421SKyle Evans if key == k then 312aedd6be5SKyle Evans sel_entry = v 313088b4f5fSWarner Losh end 314088b4f5fSWarner Losh end 315088b4f5fSWarner Losh 316088b4f5fSWarner Losh -- if we have an alias do the assigned action: 3179f71d421SKyle Evans if sel_entry ~= nil then 3188d415029SKyle Evans -- Get menu handler 319aedd6be5SKyle Evans local handler = menu.handlers[sel_entry.entry_type] 3209f71d421SKyle Evans if handler ~= nil then 3218d415029SKyle Evans -- The handler's return value indicates whether 3228d415029SKyle Evans -- we need to exit this menu. An omitted return 3238d415029SKyle Evans -- value means "continue" by default. 324aedd6be5SKyle Evans cont = handler(m, sel_entry) 3259f71d421SKyle Evans if cont == nil then 326aedd6be5SKyle Evans cont = true 327aefcaa7eSKyle Evans end 328088b4f5fSWarner Losh end 329088b4f5fSWarner Losh -- if we got an alias key the screen is out of date: 330aedd6be5SKyle Evans screen.clear() 331aedd6be5SKyle Evans screen.defcursor() 332aedd6be5SKyle Evans alias_table = drawer.drawscreen(m) 333088b4f5fSWarner Losh end 334088b4f5fSWarner Losh end 335088b4f5fSWarner Losh 33620a81676SKyle Evans if m == menu.default then 337aedd6be5SKyle Evans screen.defcursor() 338aedd6be5SKyle Evans print("Exiting menu!") 339aedd6be5SKyle Evans return false 340088b4f5fSWarner Losh end 341088b4f5fSWarner Losh 342aedd6be5SKyle Evans return true 343088b4f5fSWarner Loshend 344088b4f5fSWarner Losh 345088b4f5fSWarner Loshfunction menu.skip() 3469f71d421SKyle Evans if core.isSerialBoot() then 347aedd6be5SKyle Evans return true 348088b4f5fSWarner Losh end 349aedd6be5SKyle Evans local c = string.lower(loader.getenv("console") or "") 3509f71d421SKyle Evans if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then 351aedd6be5SKyle Evans return true 352088b4f5fSWarner Losh end 353088b4f5fSWarner Losh 354aedd6be5SKyle Evans c = string.lower(loader.getenv("beastie_disable") or "") 355aedd6be5SKyle Evans print("beastie_disable", c) 356aedd6be5SKyle Evans return c == "yes" 357088b4f5fSWarner Loshend 358088b4f5fSWarner Losh 359088b4f5fSWarner Loshfunction menu.autoboot() 36049550489SKyle Evans if menu.already_autoboot then 361aedd6be5SKyle Evans return 362088b4f5fSWarner Losh end 363aedd6be5SKyle Evans menu.already_autoboot = true 364088b4f5fSWarner Losh 365aedd6be5SKyle Evans local ab = loader.getenv("autoboot_delay") 3669f71d421SKyle Evans if ab ~= nil and ab:lower() == "no" then 367aedd6be5SKyle Evans return 3689f71d421SKyle Evans elseif tonumber(ab) == -1 then 369aedd6be5SKyle Evans core.boot() 370088b4f5fSWarner Losh end 371aedd6be5SKyle Evans ab = tonumber(ab) or 10 372088b4f5fSWarner Losh 373aedd6be5SKyle Evans local x = loader.getenv("loader_menu_timeout_x") or 5 374aedd6be5SKyle Evans local y = loader.getenv("loader_menu_timeout_y") or 22 375088b4f5fSWarner Losh 376aedd6be5SKyle Evans local endtime = loader.time() + ab 377aedd6be5SKyle Evans local time 378088b4f5fSWarner Losh 379088b4f5fSWarner Losh repeat 380aedd6be5SKyle Evans time = endtime - loader.time() 381aedd6be5SKyle Evans screen.setcursor(x, y) 38257099121SKyle Evans print("Autoboot in " .. time .. 38357099121SKyle Evans " seconds, hit [Enter] to boot" .. 384aedd6be5SKyle Evans " or any other key to stop ") 385aedd6be5SKyle Evans screen.defcursor() 3869f71d421SKyle Evans if io.ischar() then 387aedd6be5SKyle Evans local ch = io.getchar() 3889f71d421SKyle Evans if ch == core.KEY_ENTER then 389aedd6be5SKyle Evans break 390088b4f5fSWarner Losh else 391088b4f5fSWarner Losh -- erase autoboot msg 392aedd6be5SKyle Evans screen.setcursor(0, y) 393088b4f5fSWarner Losh print(" " 394aedd6be5SKyle Evans .. " ") 395aedd6be5SKyle Evans screen.defcursor() 396aedd6be5SKyle Evans return 397088b4f5fSWarner Losh end 398088b4f5fSWarner Losh end 399088b4f5fSWarner Losh 400aedd6be5SKyle Evans loader.delay(50000) 401aedd6be5SKyle Evans until time <= 0 402aedd6be5SKyle Evans core.boot() 403088b4f5fSWarner Losh 404088b4f5fSWarner Loshend 405088b4f5fSWarner Losh 406aedd6be5SKyle Evansreturn menu 407