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) 68*4f437f9eSKyle Evans local choices = entry.items 69*4f437f9eSKyle Evans if type(choices) == "function" then 70*4f437f9eSKyle Evans choices = choices() 71*4f437f9eSKyle Evans end 729f71d421SKyle Evans if #choices > 0 then 73aedd6be5SKyle Evans caridx = (caridx % #choices) + 1 74aedd6be5SKyle Evans config.setCarouselIndex(carid, caridx) 75aedd6be5SKyle Evans entry.func(caridx, choices[caridx], choices) 768d415029SKyle Evans end 778d415029SKyle Evans end, 788d415029SKyle Evans [core.MENU_SUBMENU] = function(current_menu, entry) 798d415029SKyle Evans -- recurse 809a28f948SKyle Evans return menu.run(entry.submenu) 818d415029SKyle Evans end, 828d415029SKyle Evans [core.MENU_RETURN] = function(current_menu, entry) 838d415029SKyle Evans -- allow entry to have a function/side effect 849f71d421SKyle Evans if entry.func ~= nil then 85aedd6be5SKyle Evans entry.func() 868d415029SKyle Evans end 87aedd6be5SKyle Evans return false 888d415029SKyle Evans end, 89aedd6be5SKyle Evans} 90280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome 91088b4f5fSWarner Losh 92088b4f5fSWarner Loshmenu.boot_options = { 93d8757746SKyle Evans entries = { 94088b4f5fSWarner Losh -- return to welcome menu 95088b4f5fSWarner Losh { 96a7cf0562SKyle Evans entry_type = core.MENU_RETURN, 97a51f9f0cSKyle Evans name = "Back to main menu" .. 98a51f9f0cSKyle Evans color.highlight(" [Backspace]"), 99088b4f5fSWarner Losh }, 100088b4f5fSWarner Losh -- load defaults 101088b4f5fSWarner Losh { 102a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 103a51f9f0cSKyle Evans name = "Load System " .. color.highlight("D") .. 104a51f9f0cSKyle Evans "efaults", 105a51f9f0cSKyle Evans func = core.setDefaults, 106088b4f5fSWarner Losh alias = {"d", "D"} 107088b4f5fSWarner Losh }, 108088b4f5fSWarner Losh { 109a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 110088b4f5fSWarner Losh }, 111088b4f5fSWarner Losh { 112a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 113a51f9f0cSKyle Evans name = "Boot Options:", 114088b4f5fSWarner Losh }, 115088b4f5fSWarner Losh -- acpi 116088b4f5fSWarner Losh { 117a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 118c1ab36f5SKyle Evans visible = core.isSystem386, 119088b4f5fSWarner Losh name = function() 120fd2b19b3SKyle Evans return OnOff(color.highlight("A") .. 121aedd6be5SKyle Evans "CPI :", core.acpi) 122088b4f5fSWarner Losh end, 123a51f9f0cSKyle Evans func = core.setACPI, 124088b4f5fSWarner Losh alias = {"a", "A"} 125088b4f5fSWarner Losh }, 126088b4f5fSWarner Losh -- safe mode 127088b4f5fSWarner Losh { 128a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 129088b4f5fSWarner Losh name = function() 13057099121SKyle Evans return OnOff("Safe " .. color.highlight("M") .. 131aedd6be5SKyle Evans "ode :", core.sm) 132088b4f5fSWarner Losh end, 133a51f9f0cSKyle Evans func = core.setSafeMode, 134088b4f5fSWarner Losh alias = {"m", "M"} 135088b4f5fSWarner Losh }, 136088b4f5fSWarner Losh -- single user 137088b4f5fSWarner Losh { 138a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 139088b4f5fSWarner Losh name = function() 140fd2b19b3SKyle Evans return OnOff(color.highlight("S") .. 141aedd6be5SKyle Evans "ingle user:", core.su) 142088b4f5fSWarner Losh end, 143a51f9f0cSKyle Evans func = core.setSingleUser, 144088b4f5fSWarner Losh alias = {"s", "S"} 145088b4f5fSWarner Losh }, 146088b4f5fSWarner Losh -- verbose boot 147088b4f5fSWarner Losh { 148a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 149088b4f5fSWarner Losh name = function() 150fd2b19b3SKyle Evans return OnOff(color.highlight("V") .. 151aedd6be5SKyle Evans "erbose :", core.verbose) 152088b4f5fSWarner Losh end, 153a51f9f0cSKyle Evans func = core.setVerbose, 154088b4f5fSWarner Losh alias = {"v", "V"} 155088b4f5fSWarner Losh }, 156d8757746SKyle Evans }, 157aedd6be5SKyle Evans} 158088b4f5fSWarner Losh 159088b4f5fSWarner Loshmenu.welcome = { 160303253e5SKyle Evans entries = function() 161aedd6be5SKyle Evans local menu_entries = menu.welcome.all_entries 162303253e5SKyle Evans -- Swap the first two menu items on single user boot 1639f71d421SKyle Evans if core.isSingleUserBoot() then 1649a0904b0SKyle Evans -- We'll cache the swapped menu, for performance 1659f71d421SKyle Evans if menu.welcome.swapped_menu ~= nil then 166aedd6be5SKyle Evans return menu.welcome.swapped_menu 1679a0904b0SKyle Evans end 1685c1b5165SKyle Evans -- Shallow copy the table 169aedd6be5SKyle Evans menu_entries = core.shallowCopyTable(menu_entries) 1705c1b5165SKyle Evans 1719a0904b0SKyle Evans -- Swap the first two menu entries 1724b6da14cSKyle Evans menu_entries[1], menu_entries[2] = 173aedd6be5SKyle Evans menu_entries[2], menu_entries[1] 174303253e5SKyle Evans 1759a0904b0SKyle Evans -- Then set their names to their alternate names 1769a0904b0SKyle Evans menu_entries[1].name, menu_entries[2].name = 1779a0904b0SKyle Evans menu_entries[1].alternate_name, 178aedd6be5SKyle Evans menu_entries[2].alternate_name 179aedd6be5SKyle Evans menu.welcome.swapped_menu = menu_entries 180303253e5SKyle Evans end 181aedd6be5SKyle Evans return menu_entries 182303253e5SKyle Evans end, 183303253e5SKyle Evans all_entries = { 184088b4f5fSWarner Losh -- boot multi user 185088b4f5fSWarner Losh { 186a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 187a51f9f0cSKyle Evans name = color.highlight("B") .. "oot Multi user " .. 188a51f9f0cSKyle Evans color.highlight("[Enter]"), 1895c1b5165SKyle Evans -- Not a standard menu entry function! 190a51f9f0cSKyle Evans alternate_name = color.highlight("B") .. 191a51f9f0cSKyle Evans "oot Multi user", 192088b4f5fSWarner Losh func = function() 193aedd6be5SKyle Evans core.setSingleUser(false) 194aedd6be5SKyle Evans core.boot() 195088b4f5fSWarner Losh end, 196b458bf0dSKyle Evans alias = {"b", "B"} 197088b4f5fSWarner Losh }, 198088b4f5fSWarner Losh -- boot single user 199088b4f5fSWarner Losh { 200a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 201a51f9f0cSKyle Evans name = "Boot " .. color.highlight("S") .. "ingle user", 2025c1b5165SKyle Evans -- Not a standard menu entry function! 203a51f9f0cSKyle Evans alternate_name = "Boot " .. color.highlight("S") .. 204a51f9f0cSKyle Evans "ingle user " .. color.highlight("[Enter]"), 205088b4f5fSWarner Losh func = function() 206aedd6be5SKyle Evans core.setSingleUser(true) 207aedd6be5SKyle Evans core.boot() 208088b4f5fSWarner Losh end, 209088b4f5fSWarner Losh alias = {"s", "S"} 210088b4f5fSWarner Losh }, 211088b4f5fSWarner Losh -- escape to interpreter 212088b4f5fSWarner Losh { 213a7cf0562SKyle Evans entry_type = core.MENU_RETURN, 214a51f9f0cSKyle Evans name = color.highlight("Esc") .. "ape to loader prompt", 215ef625845SKyle Evans func = function() 216aedd6be5SKyle Evans loader.setenv("autoboot_delay", "NO") 217ef625845SKyle Evans end, 21839006570SKyle Evans alias = {core.KEYSTR_ESCAPE} 219088b4f5fSWarner Losh }, 220088b4f5fSWarner Losh -- reboot 221088b4f5fSWarner Losh { 222a7cf0562SKyle Evans entry_type = core.MENU_ENTRY, 223a51f9f0cSKyle Evans name = color.highlight("R") .. "eboot", 224088b4f5fSWarner Losh func = function() 225aedd6be5SKyle Evans loader.perform("reboot") 226088b4f5fSWarner Losh end, 227088b4f5fSWarner Losh alias = {"r", "R"} 228088b4f5fSWarner Losh }, 229088b4f5fSWarner Losh { 230a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 231088b4f5fSWarner Losh }, 232088b4f5fSWarner Losh { 233a7cf0562SKyle Evans entry_type = core.MENU_SEPARATOR, 234a51f9f0cSKyle Evans name = "Options:", 235088b4f5fSWarner Losh }, 236088b4f5fSWarner Losh -- kernel options 237088b4f5fSWarner Losh { 238a7cf0562SKyle Evans entry_type = core.MENU_CAROUSEL_ENTRY, 239ada26c4aSKyle Evans carousel_id = "kernel", 240ada26c4aSKyle Evans items = core.kernelList, 241ada26c4aSKyle Evans name = function(idx, choice, all_choices) 2429f71d421SKyle Evans if #all_choices == 0 then 243aedd6be5SKyle Evans return "Kernel: " 244088b4f5fSWarner Losh end 245b1b1f2b8SKyle Evans 246aedd6be5SKyle Evans local is_default = (idx == 1) 247aedd6be5SKyle Evans local kernel_name = "" 248aedd6be5SKyle Evans local name_color 2499f71d421SKyle Evans if is_default then 250aedd6be5SKyle Evans name_color = color.escapef(color.GREEN) 251aedd6be5SKyle Evans kernel_name = "default/" 252bcf48a15SKyle Evans else 253aedd6be5SKyle Evans name_color = color.escapef(color.BLUE) 254b1b1f2b8SKyle Evans end 255fd2b19b3SKyle Evans kernel_name = kernel_name .. name_color .. 256aedd6be5SKyle Evans choice .. color.default() 257fd2b19b3SKyle Evans return color.highlight("K") .. "ernel: " .. 258fd2b19b3SKyle Evans kernel_name .. " (" .. idx .. " of " .. 259aedd6be5SKyle Evans #all_choices .. ")" 260088b4f5fSWarner Losh end, 2615d1e2f83SKyle Evans func = function(idx, choice, all_choices) 262aedd6be5SKyle Evans config.selectkernel(choice) 263088b4f5fSWarner Losh end, 264088b4f5fSWarner Losh alias = {"k", "K"} 265088b4f5fSWarner Losh }, 266088b4f5fSWarner Losh -- boot options 267088b4f5fSWarner Losh { 268a7cf0562SKyle Evans entry_type = core.MENU_SUBMENU, 269a51f9f0cSKyle Evans name = "Boot " .. color.highlight("O") .. "ptions", 2709a28f948SKyle Evans submenu = menu.boot_options, 271088b4f5fSWarner Losh alias = {"o", "O"} 272d8757746SKyle Evans }, 273d8757746SKyle Evans }, 274aedd6be5SKyle Evans} 275088b4f5fSWarner Losh 27620a81676SKyle Evansmenu.default = menu.welcome 27720a81676SKyle Evans 278088b4f5fSWarner Loshfunction menu.run(m) 279088b4f5fSWarner Losh 2809f71d421SKyle Evans if menu.skip() then 281aedd6be5SKyle Evans core.autoboot() 282aedd6be5SKyle Evans return false 283088b4f5fSWarner Losh end 284088b4f5fSWarner Losh 2859f71d421SKyle Evans if m == nil then 28620a81676SKyle Evans m = menu.default 287088b4f5fSWarner Losh end 288088b4f5fSWarner Losh 289088b4f5fSWarner Losh -- redraw screen 290aedd6be5SKyle Evans screen.clear() 291aedd6be5SKyle Evans screen.defcursor() 292aedd6be5SKyle Evans local alias_table = drawer.drawscreen(m) 293088b4f5fSWarner Losh 294aedd6be5SKyle Evans menu.autoboot() 295088b4f5fSWarner Losh 296aedd6be5SKyle Evans cont = true 2979f71d421SKyle Evans while cont do 298aedd6be5SKyle Evans local key = io.getchar() 299088b4f5fSWarner Losh 300b458bf0dSKyle Evans -- Special key behaviors 3019f71d421SKyle Evans if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and 30220a81676SKyle Evans m ~= menu.default then 303aedd6be5SKyle Evans break 3049f71d421SKyle Evans elseif key == core.KEY_ENTER then 305aedd6be5SKyle Evans core.boot() 306b458bf0dSKyle Evans -- Should not return 307abc4f7e7SKyle Evans end 308abc4f7e7SKyle Evans 309abc4f7e7SKyle Evans key = string.char(key) 310088b4f5fSWarner Losh -- check to see if key is an alias 311aedd6be5SKyle Evans local sel_entry = nil 312088b4f5fSWarner Losh for k, v in pairs(alias_table) do 3139f71d421SKyle Evans if key == k then 314aedd6be5SKyle Evans sel_entry = v 315088b4f5fSWarner Losh end 316088b4f5fSWarner Losh end 317088b4f5fSWarner Losh 318088b4f5fSWarner Losh -- if we have an alias do the assigned action: 3199f71d421SKyle Evans if sel_entry ~= nil then 3208d415029SKyle Evans -- Get menu handler 321aedd6be5SKyle Evans local handler = menu.handlers[sel_entry.entry_type] 3229f71d421SKyle Evans if handler ~= nil then 3238d415029SKyle Evans -- The handler's return value indicates whether 3248d415029SKyle Evans -- we need to exit this menu. An omitted return 3258d415029SKyle Evans -- value means "continue" by default. 326aedd6be5SKyle Evans cont = handler(m, sel_entry) 3279f71d421SKyle Evans if cont == nil then 328aedd6be5SKyle Evans cont = true 329aefcaa7eSKyle Evans end 330088b4f5fSWarner Losh end 331088b4f5fSWarner Losh -- if we got an alias key the screen is out of date: 332aedd6be5SKyle Evans screen.clear() 333aedd6be5SKyle Evans screen.defcursor() 334aedd6be5SKyle Evans alias_table = drawer.drawscreen(m) 335088b4f5fSWarner Losh end 336088b4f5fSWarner Losh end 337088b4f5fSWarner Losh 33820a81676SKyle Evans if m == menu.default then 339aedd6be5SKyle Evans screen.defcursor() 340aedd6be5SKyle Evans print("Exiting menu!") 341aedd6be5SKyle Evans return false 342088b4f5fSWarner Losh end 343088b4f5fSWarner Losh 344aedd6be5SKyle Evans return true 345088b4f5fSWarner Loshend 346088b4f5fSWarner Losh 347088b4f5fSWarner Loshfunction menu.skip() 3489f71d421SKyle Evans if core.isSerialBoot() then 349aedd6be5SKyle Evans return true 350088b4f5fSWarner Losh end 351aedd6be5SKyle Evans local c = string.lower(loader.getenv("console") or "") 3529f71d421SKyle Evans if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then 353aedd6be5SKyle Evans return true 354088b4f5fSWarner Losh end 355088b4f5fSWarner Losh 356aedd6be5SKyle Evans c = string.lower(loader.getenv("beastie_disable") or "") 357aedd6be5SKyle Evans print("beastie_disable", c) 358aedd6be5SKyle Evans return c == "yes" 359088b4f5fSWarner Loshend 360088b4f5fSWarner Losh 361088b4f5fSWarner Loshfunction menu.autoboot() 36249550489SKyle Evans if menu.already_autoboot then 363aedd6be5SKyle Evans return 364088b4f5fSWarner Losh end 365aedd6be5SKyle Evans menu.already_autoboot = true 366088b4f5fSWarner Losh 367aedd6be5SKyle Evans local ab = loader.getenv("autoboot_delay") 3689f71d421SKyle Evans if ab ~= nil and ab:lower() == "no" then 369aedd6be5SKyle Evans return 3709f71d421SKyle Evans elseif tonumber(ab) == -1 then 371aedd6be5SKyle Evans core.boot() 372088b4f5fSWarner Losh end 373aedd6be5SKyle Evans ab = tonumber(ab) or 10 374088b4f5fSWarner Losh 375aedd6be5SKyle Evans local x = loader.getenv("loader_menu_timeout_x") or 5 376aedd6be5SKyle Evans local y = loader.getenv("loader_menu_timeout_y") or 22 377088b4f5fSWarner Losh 378aedd6be5SKyle Evans local endtime = loader.time() + ab 379aedd6be5SKyle Evans local time 380088b4f5fSWarner Losh 381088b4f5fSWarner Losh repeat 382aedd6be5SKyle Evans time = endtime - loader.time() 383aedd6be5SKyle Evans screen.setcursor(x, y) 38457099121SKyle Evans print("Autoboot in " .. time .. 38557099121SKyle Evans " seconds, hit [Enter] to boot" .. 386aedd6be5SKyle Evans " or any other key to stop ") 387aedd6be5SKyle Evans screen.defcursor() 3889f71d421SKyle Evans if io.ischar() then 389aedd6be5SKyle Evans local ch = io.getchar() 3909f71d421SKyle Evans if ch == core.KEY_ENTER then 391aedd6be5SKyle Evans break 392088b4f5fSWarner Losh else 393088b4f5fSWarner Losh -- erase autoboot msg 394aedd6be5SKyle Evans screen.setcursor(0, y) 395088b4f5fSWarner Losh print(" " 396aedd6be5SKyle Evans .. " ") 397aedd6be5SKyle Evans screen.defcursor() 398aedd6be5SKyle Evans return 399088b4f5fSWarner Losh end 400088b4f5fSWarner Losh end 401088b4f5fSWarner Losh 402aedd6be5SKyle Evans loader.delay(50000) 403aedd6be5SKyle Evans until time <= 0 404aedd6be5SKyle Evans core.boot() 405088b4f5fSWarner Losh 406088b4f5fSWarner Loshend 407088b4f5fSWarner Losh 408aedd6be5SKyle Evansreturn menu 409