1-- 2-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 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", "\013"} 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 -- Exit on backspace 276 if (key == 127) and (m ~= menu.welcome) then 277 break 278 end 279 280 key = string.char(key) 281 -- check to see if key is an alias 282 local sel_entry = nil; 283 for k, v in pairs(alias_table) do 284 if (key == k) then 285 sel_entry = v; 286 end 287 end 288 289 -- if we have an alias do the assigned action: 290 if(sel_entry ~= nil) then 291 if (sel_entry.entry_type == "entry") then 292 -- run function 293 sel_entry.func(); 294 elseif (sel_entry.entry_type == "submenu") then 295 -- recurse 296 cont = menu.run(sel_entry.submenu()); 297 elseif (sel_entry.entry_type == "return") then 298 -- break recurse 299 cont = false; 300 end 301 -- if we got an alias key the screen is out of date: 302 screen.clear(); 303 screen.defcursor(); 304 alias_table = drawer.drawscreen(m); 305 end 306 end 307 308 if (m == menu.welcome) then 309 screen.defcursor(); 310 print("Exiting menu!"); 311 return false; 312 end 313 314 return true; 315end 316 317function menu.skip() 318 if core.bootserial() then 319 return true; 320 end 321 local c = string.lower(loader.getenv("console") or ""); 322 if (c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil then 323 return true; 324 end 325 326 c = string.lower(loader.getenv("beastie_disable") or ""); 327 print("beastie_disable", c); 328 return c == "yes"; 329end 330 331function menu.autoboot() 332 if menu.already_autoboot == true then 333 return; 334 end 335 menu.already_autoboot = true; 336 337 local ab = loader.getenv("autoboot_delay"); 338 if ab == "NO" or ab == "no" then 339 core.boot(); 340 end 341 ab = tonumber(ab) or 10; 342 343 local x = loader.getenv("loader_menu_timeout_x") or 5; 344 local y = loader.getenv("loader_menu_timeout_y") or 22; 345 346 local endtime = loader.time() + ab; 347 local time; 348 349 repeat 350 time = endtime - loader.time(); 351 screen.setcursor(x, y); 352 print("Autoboot in "..time.." seconds, hit [Enter] to boot" 353 .." or any other key to stop "); 354 screen.defcursor(); 355 if io.ischar() then 356 local ch = io.getchar(); 357 if ch == 13 then 358 break; 359 else 360 -- prevent autoboot when escaping to interpreter 361 loader.setenv("autoboot_delay", "NO"); 362 -- erase autoboot msg 363 screen.setcursor(0, y); 364 print(" " 365 .." "); 366 screen.defcursor(); 367 return; 368 end 369 end 370 371 loader.delay(50000); 372 until time <= 0 373 core.boot(); 374 375end 376 377function OnOff(str, b) 378 if (b) then 379 return str .. color.escapef(color.GREEN).."On"..color.escapef(color.WHITE); 380 else 381 return str .. color.escapef(color.RED).."off"..color.escapef(color.WHITE); 382 end 383end 384 385return menu 386