1-- 2-- Copyright (c) 2015 Pedro Souza <[email protected]> 3-- All rights reserved. 4-- 5-- Redistribution and use in source and binary forms, with or without 6-- modification, are permitted provided that the following conditions 7-- are met: 8-- 1. Redistributions of source code must retain the above copyright 9-- notice, this list of conditions and the following disclaimer. 10-- 2. Redistributions in binary form must reproduce the above copyright 11-- notice, this list of conditions and the following disclaimer in the 12-- documentation and/or other materials provided with the distribution. 13-- 14-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24-- SUCH DAMAGE. 25-- 26-- $FreeBSD$ 27-- 28 29 30local menu = {}; 31 32local core = require("core"); 33local color = require("color"); 34local config = require("config"); 35local screen = require("screen"); 36local drawer = require("drawer"); 37 38local OnOff; 39local skip; 40local run; 41local autoboot; 42 43--loader menu tree: 44--rooted at menu.welcome 45--submenu declarations: 46local kernel_options; 47local boot_options; 48local welcome; 49 50menu.kernel_options = { 51 -- this table is dynamically appended to when accessed 52 -- return to welcome menu 53 { 54 entry_type = "return", 55 name = function() 56 return "Back to main menu"..color.highlight(" [Backspace]"); 57 end, 58 alias = {"\08"} 59 } 60}; 61 62menu.boot_options = { 63 -- return to welcome menu 64 { 65 entry_type = "return", 66 name = function() 67 return "Back to main menu"..color.highlight(" [Backspace]"); 68 end, 69 alias = {"\08"} 70 }, 71 72 -- load defaults 73 { 74 entry_type = "entry", 75 name = function() 76 return "Load System "..color.highlight("D").."efaults"; 77 end, 78 func = function() 79 core.setDefaults() 80 end, 81 alias = {"d", "D"} 82 }, 83 84 { 85 entry_type = "separator", 86 name = function() 87 return ""; 88 end 89 }, 90 91 { 92 entry_type = "separator", 93 name = function() 94 return "Boot Options:"; 95 end 96 }, 97 98 -- acpi 99 { 100 entry_type = "entry", 101 name = function() 102 return OnOff(color.highlight("A").."CPI :", core.acpi); 103 end, 104 func = function() 105 core.setACPI(); 106 end, 107 alias = {"a", "A"} 108 }, 109 -- safe mode 110 { 111 entry_type = "entry", 112 name = function() 113 return OnOff("Safe "..color.highlight("M").."ode :", core.sm); 114 end, 115 func = function() 116 core.setSafeMode(); 117 end, 118 alias = {"m", "M"} 119 }, 120 -- single user 121 { 122 entry_type = "entry", 123 name = function() 124 return OnOff(color.highlight("S").."ingle user:", core.su); 125 end, 126 func = function() 127 core.setSingleUser(); 128 end, 129 alias = {"s", "S"} 130 }, 131 -- verbose boot 132 { 133 entry_type = "entry", 134 name = function() 135 return OnOff(color.highlight("V").."erbose :", core.verbose); 136 end, 137 func = function() 138 core.setVerbose(); 139 end, 140 alias = {"v", "V"} 141 }, 142}; 143 144menu.welcome = { 145 -- boot multi user 146 { 147 entry_type = "entry", 148 name = function() 149 return color.highlight("B").."oot Multi user "..color.highlight("[Enter]"); 150 end, 151 func = function() 152 core.setSingleUser(false); 153 core.boot(); 154 end, 155 alias = {"b", "B"} 156 }, 157 158 -- boot single user 159 { 160 entry_type = "entry", 161 name = function() 162 return "Boot "..color.highlight("S").."ingle user"; 163 end, 164 func = function() 165 core.setSingleUser(true); 166 core.boot(); 167 end, 168 alias = {"s", "S"} 169 }, 170 171 -- escape to interpreter 172 { 173 entry_type = "return", 174 name = function() 175 return color.highlight("Esc").."ape to lua interpreter"; 176 end, 177 alias = {"\027"} 178 }, 179 180 -- reboot 181 { 182 entry_type = "entry", 183 name = function() 184 return color.highlight("R").."eboot"; 185 end, 186 func = function() 187 loader.perform("reboot"); 188 end, 189 alias = {"r", "R"} 190 }, 191 192 193 { 194 entry_type = "separator", 195 name = function() 196 return ""; 197 end 198 }, 199 200 { 201 entry_type = "separator", 202 name = function() 203 return "Options:"; 204 end 205 }, 206 207 -- kernel options 208 { 209 entry_type = "submenu", 210 name = function() 211 local kernels = core.kernelList(); 212 if #kernels == 0 then 213 return "Kernels (not available)"; 214 end 215 return color.highlight("K").."ernels"; 216 end, 217 submenu = function() 218 219 -- dynamically build the kernel menu: 220 local kernels = core.kernelList(); 221 for k, v in ipairs(kernels) do 222 menu.kernel_options[#menu.kernel_options + 1] = { 223 entry_type = "entry", 224 name = function() 225 return v; 226 end, 227 func = function() 228 config.reload(v); 229 end, 230 alias = {} -- automatically enumerated 231 } 232 end 233 234 return menu.kernel_options; 235 end, 236 alias = {"k", "K"} 237 }, 238 239 -- boot options 240 { 241 entry_type = "submenu", 242 name = function() 243 return "Boot "..color.highlight("O").."ptions"; 244 end, 245 submenu = function() 246 return menu.boot_options; 247 end, 248 alias = {"o", "O"} 249 } 250 251}; 252 253function menu.run(m) 254 255 if (menu.skip()) then 256 core.autoboot(); 257 return false; 258 end 259 260 if (m == nil) then 261 m = menu.welcome; 262 end 263 264 -- redraw screen 265 screen.clear(); 266 screen.defcursor(); 267 local alias_table = drawer.drawscreen(m); 268 269-- menu.autoboot(); 270 271 cont = true; 272 while cont do 273 local key = io.getchar(); 274 275 -- Special key behaviors 276 if (key == 127) and (m ~= menu.welcome) then 277 break 278 elseif (key == 13) then 279 core.boot(); 280 -- Should not return 281 end 282 283 key = string.char(key) 284 -- check to see if key is an alias 285 local sel_entry = nil; 286 for k, v in pairs(alias_table) do 287 if (key == k) then 288 sel_entry = v; 289 end 290 end 291 292 -- if we have an alias do the assigned action: 293 if(sel_entry ~= nil) then 294 if (sel_entry.entry_type == "entry") then 295 -- run function 296 sel_entry.func(); 297 elseif (sel_entry.entry_type == "submenu") then 298 -- recurse 299 cont = menu.run(sel_entry.submenu()); 300 elseif (sel_entry.entry_type == "return") then 301 -- break recurse 302 cont = false; 303 end 304 -- if we got an alias key the screen is out of date: 305 screen.clear(); 306 screen.defcursor(); 307 alias_table = drawer.drawscreen(m); 308 end 309 end 310 311 if (m == menu.welcome) then 312 screen.defcursor(); 313 print("Exiting menu!"); 314 return false; 315 end 316 317 return true; 318end 319 320function menu.skip() 321 if core.bootserial() then 322 return true; 323 end 324 local c = string.lower(loader.getenv("console") or ""); 325 if (c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil then 326 return true; 327 end 328 329 c = string.lower(loader.getenv("beastie_disable") or ""); 330 print("beastie_disable", c); 331 return c == "yes"; 332end 333 334function menu.autoboot() 335 if menu.already_autoboot == true then 336 return; 337 end 338 menu.already_autoboot = true; 339 340 local ab = loader.getenv("autoboot_delay"); 341 if ab == "NO" or ab == "no" then 342 core.boot(); 343 end 344 ab = tonumber(ab) or 10; 345 346 local x = loader.getenv("loader_menu_timeout_x") or 5; 347 local y = loader.getenv("loader_menu_timeout_y") or 22; 348 349 local endtime = loader.time() + ab; 350 local time; 351 352 repeat 353 time = endtime - loader.time(); 354 screen.setcursor(x, y); 355 print("Autoboot in "..time.." seconds, hit [Enter] to boot" 356 .." or any other key to stop "); 357 screen.defcursor(); 358 if io.ischar() then 359 local ch = io.getchar(); 360 if ch == 13 then 361 break; 362 else 363 -- prevent autoboot when escaping to interpreter 364 loader.setenv("autoboot_delay", "NO"); 365 -- erase autoboot msg 366 screen.setcursor(0, y); 367 print(" " 368 .." "); 369 screen.defcursor(); 370 return; 371 end 372 end 373 374 loader.delay(50000); 375 until time <= 0 376 core.boot(); 377 378end 379 380function OnOff(str, b) 381 if (b) then 382 return str .. color.escapef(color.GREEN).."On"..color.escapef(color.WHITE); 383 else 384 return str .. color.escapef(color.RED).."off"..color.escapef(color.WHITE); 385 end 386end 387 388return menu 389