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; 42local current_kernel_index = 1; 43 44--loader menu tree: 45--rooted at menu.welcome 46--submenu declarations: 47local boot_options; 48local welcome; 49 50menu.boot_options = { 51 -- return to welcome menu 52 { 53 entry_type = "return", 54 name = function() 55 return "Back to main menu"..color.highlight(" [Backspace]"); 56 end 57 }, 58 59 -- load defaults 60 { 61 entry_type = "entry", 62 name = function() 63 return "Load System "..color.highlight("D").."efaults"; 64 end, 65 func = function() 66 core.setDefaults() 67 end, 68 alias = {"d", "D"} 69 }, 70 71 { 72 entry_type = "separator", 73 name = function() 74 return ""; 75 end 76 }, 77 78 { 79 entry_type = "separator", 80 name = function() 81 return "Boot Options:"; 82 end 83 }, 84 85 -- acpi 86 { 87 entry_type = "entry", 88 name = function() 89 return OnOff(color.highlight("A").."CPI :", core.acpi); 90 end, 91 func = function() 92 core.setACPI(); 93 end, 94 alias = {"a", "A"} 95 }, 96 -- safe mode 97 { 98 entry_type = "entry", 99 name = function() 100 return OnOff("Safe "..color.highlight("M").."ode :", core.sm); 101 end, 102 func = function() 103 core.setSafeMode(); 104 end, 105 alias = {"m", "M"} 106 }, 107 -- single user 108 { 109 entry_type = "entry", 110 name = function() 111 return OnOff(color.highlight("S").."ingle user:", core.su); 112 end, 113 func = function() 114 core.setSingleUser(); 115 end, 116 alias = {"s", "S"} 117 }, 118 -- verbose boot 119 { 120 entry_type = "entry", 121 name = function() 122 return OnOff(color.highlight("V").."erbose :", core.verbose); 123 end, 124 func = function() 125 core.setVerbose(); 126 end, 127 alias = {"v", "V"} 128 }, 129}; 130 131menu.welcome = { 132 -- boot multi user 133 { 134 entry_type = "entry", 135 name = function() 136 return color.highlight("B").."oot Multi user "..color.highlight("[Enter]"); 137 end, 138 func = function() 139 core.setSingleUser(false); 140 core.boot(); 141 end, 142 alias = {"b", "B"} 143 }, 144 145 -- boot single user 146 { 147 entry_type = "entry", 148 name = function() 149 return "Boot "..color.highlight("S").."ingle user"; 150 end, 151 func = function() 152 core.setSingleUser(true); 153 core.boot(); 154 end, 155 alias = {"s", "S"} 156 }, 157 158 -- escape to interpreter 159 { 160 entry_type = "return", 161 name = function() 162 return color.highlight("Esc").."ape to loader prompt"; 163 end, 164 alias = {core.KEYSTR_ESCAPE} 165 }, 166 167 -- reboot 168 { 169 entry_type = "entry", 170 name = function() 171 return color.highlight("R").."eboot"; 172 end, 173 func = function() 174 loader.perform("reboot"); 175 end, 176 alias = {"r", "R"} 177 }, 178 179 180 { 181 entry_type = "separator", 182 name = function() 183 return ""; 184 end 185 }, 186 187 { 188 entry_type = "separator", 189 name = function() 190 return "Options:"; 191 end 192 }, 193 194 -- kernel options 195 { 196 entry_type = "entry", 197 name = function() 198 local kernels = core.kernelList(); 199 if #kernels == 0 then 200 return "Kernel: "; 201 end 202 203 local kernel_name = color.escapef(color.GREEN) .. 204 kernels[current_kernel_index] .. color.default(); 205 if (current_kernel_index == 1) then 206 kernel_name = "default/" .. kernel_name; 207 end 208 return color.highlight("K").."ernel: " .. kernel_name .. 209 " (" .. current_kernel_index .. 210 " of " .. #kernels .. ")"; 211 end, 212 func = function() 213 214 -- dynamically build the kernel menu: 215 local kernels = core.kernelList(); 216 -- Don't do anything if we don't have multiple kernels 217 if #kernels <= 1 then 218 return nil; 219 end 220 current_kernel_index = (current_kernel_index % #kernels) 221 + 1; 222 local current_kernel = kernels[current_kernel_index]; 223 config.reload(current_kernel) 224 end, 225 alias = {"k", "K"} 226 }, 227 228 -- boot options 229 { 230 entry_type = "submenu", 231 name = function() 232 return "Boot "..color.highlight("O").."ptions"; 233 end, 234 submenu = function() 235 return menu.boot_options; 236 end, 237 alias = {"o", "O"} 238 } 239 240}; 241 242function menu.run(m) 243 244 if (menu.skip()) then 245 core.autoboot(); 246 return false; 247 end 248 249 if (m == nil) then 250 m = menu.welcome; 251 end 252 253 -- redraw screen 254 screen.clear(); 255 screen.defcursor(); 256 local alias_table = drawer.drawscreen(m); 257 258-- menu.autoboot(); 259 260 cont = true; 261 while cont do 262 local key = io.getchar(); 263 264 -- Special key behaviors 265 if (key == core.KEY_BACKSPACE) and (m ~= menu.welcome) then 266 break 267 elseif (key == core.KEY_ENTER) then 268 core.boot(); 269 -- Should not return 270 end 271 272 key = string.char(key) 273 -- check to see if key is an alias 274 local sel_entry = nil; 275 for k, v in pairs(alias_table) do 276 if (key == k) then 277 sel_entry = v; 278 end 279 end 280 281 -- if we have an alias do the assigned action: 282 if(sel_entry ~= nil) then 283 if (sel_entry.entry_type == "entry") then 284 -- run function 285 sel_entry.func(); 286 elseif (sel_entry.entry_type == "submenu") then 287 -- recurse 288 cont = menu.run(sel_entry.submenu()); 289 elseif (sel_entry.entry_type == "return") then 290 -- break recurse 291 cont = false; 292 end 293 -- if we got an alias key the screen is out of date: 294 screen.clear(); 295 screen.defcursor(); 296 alias_table = drawer.drawscreen(m); 297 end 298 end 299 300 if (m == menu.welcome) then 301 screen.defcursor(); 302 print("Exiting menu!"); 303 return false; 304 end 305 306 return true; 307end 308 309function menu.skip() 310 if core.bootserial() then 311 return true; 312 end 313 local c = string.lower(loader.getenv("console") or ""); 314 if (c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil then 315 return true; 316 end 317 318 c = string.lower(loader.getenv("beastie_disable") or ""); 319 print("beastie_disable", c); 320 return c == "yes"; 321end 322 323function menu.autoboot() 324 if menu.already_autoboot == true then 325 return; 326 end 327 menu.already_autoboot = true; 328 329 local ab = loader.getenv("autoboot_delay"); 330 if ab == "NO" or ab == "no" then 331 core.boot(); 332 end 333 ab = tonumber(ab) or 10; 334 335 local x = loader.getenv("loader_menu_timeout_x") or 5; 336 local y = loader.getenv("loader_menu_timeout_y") or 22; 337 338 local endtime = loader.time() + ab; 339 local time; 340 341 repeat 342 time = endtime - loader.time(); 343 screen.setcursor(x, y); 344 print("Autoboot in "..time.." seconds, hit [Enter] to boot" 345 .." or any other key to stop "); 346 screen.defcursor(); 347 if io.ischar() then 348 local ch = io.getchar(); 349 if ch == core.KEY_ENTER then 350 break; 351 else 352 -- prevent autoboot when escaping to interpreter 353 loader.setenv("autoboot_delay", "NO"); 354 -- erase autoboot msg 355 screen.setcursor(0, y); 356 print(" " 357 .." "); 358 screen.defcursor(); 359 return; 360 end 361 end 362 363 loader.delay(50000); 364 until time <= 0 365 core.boot(); 366 367end 368 369function OnOff(str, b) 370 if (b) then 371 return str .. color.escapef(color.GREEN).."On"..color.escapef(color.WHITE); 372 else 373 return str .. color.escapef(color.RED).."off"..color.escapef(color.WHITE); 374 end 375end 376 377return menu 378