2018-02-12 15:32:00 +00:00
|
|
|
--
|
|
|
|
-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
|
|
|
|
-- All rights reserved.
|
|
|
|
--
|
|
|
|
-- Redistribution and use in source and binary forms, with or without
|
|
|
|
-- modification, are permitted provided that the following conditions
|
|
|
|
-- are met:
|
|
|
|
-- 1. Redistributions of source code must retain the above copyright
|
|
|
|
-- notice, this list of conditions and the following disclaimer.
|
|
|
|
-- 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
-- notice, this list of conditions and the following disclaimer in the
|
|
|
|
-- documentation and/or other materials provided with the distribution.
|
|
|
|
--
|
|
|
|
-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
-- SUCH DAMAGE.
|
|
|
|
--
|
|
|
|
-- $FreeBSD$
|
|
|
|
--
|
|
|
|
|
|
|
|
|
|
|
|
local menu = {};
|
|
|
|
|
|
|
|
local core = require("core");
|
|
|
|
local color = require("color");
|
|
|
|
local config = require("config");
|
|
|
|
local screen = require("screen");
|
|
|
|
local drawer = require("drawer");
|
|
|
|
|
|
|
|
local OnOff;
|
|
|
|
local skip;
|
|
|
|
local run;
|
|
|
|
local autoboot;
|
2018-02-16 14:39:41 +00:00
|
|
|
local carousel_choices = {};
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 15:49:27 +00:00
|
|
|
-- loader menu tree is rooted at menu.welcome
|
2018-02-12 15:32:00 +00:00
|
|
|
|
|
|
|
menu.boot_options = {
|
2018-02-19 16:25:43 +00:00
|
|
|
entries = {
|
|
|
|
-- return to welcome menu
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_RETURN,
|
|
|
|
name = function()
|
|
|
|
return "Back to main menu" ..
|
|
|
|
color.highlight(" [Backspace]");
|
|
|
|
end
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
-- load defaults
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return "Load System " .. color.highlight("D") ..
|
|
|
|
"efaults";
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setDefaults();
|
|
|
|
end,
|
|
|
|
alias = {"d", "D"}
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
{
|
|
|
|
entry_type = core.MENU_SEPARATOR,
|
|
|
|
name = function()
|
|
|
|
return "";
|
|
|
|
end
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
{
|
|
|
|
entry_type = core.MENU_SEPARATOR,
|
|
|
|
name = function()
|
|
|
|
return "Boot Options:";
|
|
|
|
end
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
-- acpi
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return OnOff(color.highlight("A") .. "CPI :",
|
|
|
|
core.acpi);
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setACPI();
|
|
|
|
end,
|
|
|
|
alias = {"a", "A"}
|
|
|
|
},
|
|
|
|
-- safe mode
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return OnOff("Safe " .. color.highlight("M") ..
|
|
|
|
"ode :", core.sm);
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setSafeMode();
|
|
|
|
end,
|
|
|
|
alias = {"m", "M"}
|
|
|
|
},
|
|
|
|
-- single user
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return OnOff(color.highlight("S") .. "ingle user:",
|
|
|
|
core.su);
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setSingleUser();
|
|
|
|
end,
|
|
|
|
alias = {"s", "S"}
|
|
|
|
},
|
|
|
|
-- verbose boot
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return OnOff(color.highlight("V") .. "erbose :",
|
|
|
|
core.verbose);
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setVerbose();
|
|
|
|
end,
|
|
|
|
alias = {"v", "V"}
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
menu.welcome = {
|
2018-02-19 16:25:43 +00:00
|
|
|
entries = {
|
|
|
|
-- boot multi user
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return color.highlight("B") .. "oot Multi user " ..
|
|
|
|
color.highlight("[Enter]");
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setSingleUser(false);
|
|
|
|
core.boot();
|
|
|
|
end,
|
|
|
|
alias = {"b", "B"}
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
-- boot single user
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return "Boot " .. color.highlight("S") .. "ingle user";
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
core.setSingleUser(true);
|
|
|
|
core.boot();
|
|
|
|
end,
|
|
|
|
alias = {"s", "S"}
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
-- escape to interpreter
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_RETURN,
|
|
|
|
name = function()
|
|
|
|
return color.highlight("Esc") .. "ape to loader prompt";
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
loader.setenv("autoboot_delay", "NO");
|
|
|
|
end,
|
|
|
|
alias = {core.KEYSTR_ESCAPE}
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
-- reboot
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_ENTRY,
|
|
|
|
name = function()
|
|
|
|
return color.highlight("R") .. "eboot";
|
|
|
|
end,
|
|
|
|
func = function()
|
|
|
|
loader.perform("reboot");
|
|
|
|
end,
|
|
|
|
alias = {"r", "R"}
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
{
|
|
|
|
entry_type = core.MENU_SEPARATOR,
|
|
|
|
name = function()
|
|
|
|
return "";
|
2018-02-16 04:31:09 +00:00
|
|
|
end
|
2018-02-19 16:25:43 +00:00
|
|
|
},
|
2018-02-16 04:31:09 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
{
|
|
|
|
entry_type = core.MENU_SEPARATOR,
|
|
|
|
name = function()
|
|
|
|
return "Options:";
|
2018-02-12 15:32:00 +00:00
|
|
|
end
|
2018-02-19 16:25:43 +00:00
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
-- kernel options
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_CAROUSEL_ENTRY,
|
|
|
|
carousel_id = "kernel",
|
|
|
|
items = core.kernelList,
|
|
|
|
name = function(idx, choice, all_choices)
|
|
|
|
if (#all_choices == 0) then
|
|
|
|
return "Kernel: ";
|
|
|
|
end
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-19 16:25:43 +00:00
|
|
|
local is_default = (idx == 1);
|
|
|
|
local kernel_name = "";
|
|
|
|
local name_color;
|
|
|
|
if (is_default) then
|
|
|
|
name_color = color.escapef(color.GREEN);
|
|
|
|
kernel_name = "default/";
|
|
|
|
else
|
|
|
|
name_color = color.escapef(color.BLUE);
|
|
|
|
end
|
|
|
|
kernel_name = kernel_name .. name_color .. choice ..
|
|
|
|
color.default();
|
|
|
|
return color.highlight("K").."ernel: " .. kernel_name ..
|
|
|
|
" (" .. idx ..
|
|
|
|
" of " .. #all_choices .. ")";
|
|
|
|
end,
|
|
|
|
func = function(idx, choice, all_choices)
|
|
|
|
config.selectkernel(choice);
|
|
|
|
end,
|
|
|
|
alias = {"k", "K"}
|
|
|
|
},
|
|
|
|
|
|
|
|
-- boot options
|
|
|
|
{
|
|
|
|
entry_type = core.MENU_SUBMENU,
|
|
|
|
name = function()
|
|
|
|
return "Boot " .. color.highlight("O") .. "ptions";
|
|
|
|
end,
|
|
|
|
submenu = function()
|
|
|
|
return menu.boot_options;
|
|
|
|
end,
|
|
|
|
alias = {"o", "O"}
|
|
|
|
},
|
|
|
|
},
|
2018-02-12 15:32:00 +00:00
|
|
|
};
|
|
|
|
|
2018-02-16 14:39:41 +00:00
|
|
|
-- The first item in every carousel is always the default item.
|
|
|
|
function menu.getCarouselIndex(id)
|
|
|
|
local val = carousel_choices[id];
|
|
|
|
if (val == nil) then
|
|
|
|
return 1;
|
|
|
|
end
|
|
|
|
return val;
|
|
|
|
end
|
|
|
|
|
|
|
|
function menu.setCarouselIndex(id, idx)
|
|
|
|
carousel_choices[id] = idx;
|
|
|
|
end
|
|
|
|
|
2018-02-12 15:32:00 +00:00
|
|
|
function menu.run(m)
|
|
|
|
|
|
|
|
if (menu.skip()) then
|
|
|
|
core.autoboot();
|
|
|
|
return false;
|
|
|
|
end
|
|
|
|
|
|
|
|
if (m == nil) then
|
|
|
|
m = menu.welcome;
|
|
|
|
end
|
|
|
|
|
|
|
|
-- redraw screen
|
|
|
|
screen.clear();
|
|
|
|
screen.defcursor();
|
|
|
|
local alias_table = drawer.drawscreen(m);
|
|
|
|
|
2018-02-17 03:13:05 +00:00
|
|
|
menu.autoboot();
|
2018-02-12 15:32:00 +00:00
|
|
|
|
|
|
|
cont = true;
|
2018-02-17 05:52:25 +00:00
|
|
|
while (cont) do
|
2018-02-14 20:18:23 +00:00
|
|
|
local key = io.getchar();
|
2018-02-12 15:32:00 +00:00
|
|
|
|
2018-02-15 18:16:16 +00:00
|
|
|
-- Special key behaviors
|
2018-02-16 17:42:38 +00:00
|
|
|
if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
|
|
|
|
(m ~= menu.welcome) then
|
2018-02-14 20:18:23 +00:00
|
|
|
break
|
2018-02-16 03:12:24 +00:00
|
|
|
elseif (key == core.KEY_ENTER) then
|
2018-02-15 18:16:16 +00:00
|
|
|
core.boot();
|
|
|
|
-- Should not return
|
2018-02-14 20:18:23 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
key = string.char(key)
|
2018-02-12 15:32:00 +00:00
|
|
|
-- check to see if key is an alias
|
|
|
|
local sel_entry = nil;
|
|
|
|
for k, v in pairs(alias_table) do
|
|
|
|
if (key == k) then
|
|
|
|
sel_entry = v;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- if we have an alias do the assigned action:
|
2018-02-17 05:52:25 +00:00
|
|
|
if (sel_entry ~= nil) then
|
2018-02-16 14:57:42 +00:00
|
|
|
if (sel_entry.entry_type == core.MENU_ENTRY) then
|
2018-02-12 15:32:00 +00:00
|
|
|
-- run function
|
|
|
|
sel_entry.func();
|
2018-02-16 14:57:42 +00:00
|
|
|
elseif (sel_entry.entry_type == core.MENU_CAROUSEL_ENTRY) then
|
2018-02-16 14:39:41 +00:00
|
|
|
-- carousel (rotating) functionality
|
|
|
|
local carid = sel_entry.carousel_id;
|
|
|
|
local caridx = menu.getCarouselIndex(carid);
|
|
|
|
local choices = sel_entry.items();
|
|
|
|
|
2018-02-16 22:51:08 +00:00
|
|
|
if (#choices > 0) then
|
|
|
|
caridx = (caridx % #choices) + 1;
|
|
|
|
menu.setCarouselIndex(carid, caridx);
|
2018-02-16 23:59:50 +00:00
|
|
|
sel_entry.func(caridx, choices[caridx],
|
2018-02-16 22:57:52 +00:00
|
|
|
choices);
|
2018-02-16 22:51:08 +00:00
|
|
|
end
|
2018-02-16 14:57:42 +00:00
|
|
|
elseif (sel_entry.entry_type == core.MENU_SUBMENU) then
|
2018-02-12 15:32:00 +00:00
|
|
|
-- recurse
|
|
|
|
cont = menu.run(sel_entry.submenu());
|
2018-02-16 14:57:42 +00:00
|
|
|
elseif (sel_entry.entry_type == core.MENU_RETURN) then
|
2018-02-16 22:17:30 +00:00
|
|
|
-- allow entry to have a function/side effect
|
|
|
|
if (sel_entry.func ~= nil) then
|
|
|
|
sel_entry.func();
|
|
|
|
end
|
2018-02-12 15:32:00 +00:00
|
|
|
-- break recurse
|
|
|
|
cont = false;
|
|
|
|
end
|
|
|
|
-- if we got an alias key the screen is out of date:
|
|
|
|
screen.clear();
|
|
|
|
screen.defcursor();
|
|
|
|
alias_table = drawer.drawscreen(m);
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if (m == menu.welcome) then
|
|
|
|
screen.defcursor();
|
|
|
|
print("Exiting menu!");
|
2018-02-19 14:21:56 +00:00
|
|
|
config.loadelf();
|
2018-02-12 15:32:00 +00:00
|
|
|
return false;
|
|
|
|
end
|
|
|
|
|
|
|
|
return true;
|
|
|
|
end
|
|
|
|
|
|
|
|
function menu.skip()
|
2018-02-19 15:47:09 +00:00
|
|
|
if (core.isSerialBoot()) then
|
2018-02-12 15:32:00 +00:00
|
|
|
return true;
|
|
|
|
end
|
|
|
|
local c = string.lower(loader.getenv("console") or "");
|
2018-02-17 05:52:25 +00:00
|
|
|
if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
|
2018-02-12 15:32:00 +00:00
|
|
|
return true;
|
|
|
|
end
|
|
|
|
|
|
|
|
c = string.lower(loader.getenv("beastie_disable") or "");
|
|
|
|
print("beastie_disable", c);
|
|
|
|
return c == "yes";
|
|
|
|
end
|
|
|
|
|
|
|
|
function menu.autoboot()
|
2018-02-17 05:52:25 +00:00
|
|
|
if (menu.already_autoboot == true) then
|
2018-02-12 15:32:00 +00:00
|
|
|
return;
|
|
|
|
end
|
|
|
|
menu.already_autoboot = true;
|
|
|
|
|
|
|
|
local ab = loader.getenv("autoboot_delay");
|
2018-02-17 03:39:55 +00:00
|
|
|
if (ab ~= nil) and (ab:lower() == "no") then
|
|
|
|
return;
|
|
|
|
elseif (tonumber(ab) == -1) then
|
2018-02-12 15:32:00 +00:00
|
|
|
core.boot();
|
|
|
|
end
|
|
|
|
ab = tonumber(ab) or 10;
|
|
|
|
|
|
|
|
local x = loader.getenv("loader_menu_timeout_x") or 5;
|
|
|
|
local y = loader.getenv("loader_menu_timeout_y") or 22;
|
|
|
|
|
|
|
|
local endtime = loader.time() + ab;
|
|
|
|
local time;
|
|
|
|
|
|
|
|
repeat
|
|
|
|
time = endtime - loader.time();
|
|
|
|
screen.setcursor(x, y);
|
2018-02-19 15:47:09 +00:00
|
|
|
print("Autoboot in " .. time ..
|
|
|
|
" seconds, hit [Enter] to boot" ..
|
|
|
|
" or any other key to stop ");
|
2018-02-12 15:32:00 +00:00
|
|
|
screen.defcursor();
|
2018-02-17 05:52:25 +00:00
|
|
|
if (io.ischar()) then
|
2018-02-12 15:32:00 +00:00
|
|
|
local ch = io.getchar();
|
2018-02-17 05:52:25 +00:00
|
|
|
if (ch == core.KEY_ENTER) then
|
2018-02-12 15:32:00 +00:00
|
|
|
break;
|
|
|
|
else
|
|
|
|
-- erase autoboot msg
|
|
|
|
screen.setcursor(0, y);
|
|
|
|
print(" "
|
2018-02-19 15:47:09 +00:00
|
|
|
.. " ");
|
2018-02-12 15:32:00 +00:00
|
|
|
screen.defcursor();
|
|
|
|
return;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
loader.delay(50000);
|
|
|
|
until time <= 0
|
|
|
|
core.boot();
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
function OnOff(str, b)
|
|
|
|
if (b) then
|
2018-02-19 15:47:09 +00:00
|
|
|
return str .. color.escapef(color.GREEN) .. "On" ..
|
|
|
|
color.escapef(color.WHITE);
|
2018-02-12 15:32:00 +00:00
|
|
|
else
|
2018-02-19 15:47:09 +00:00
|
|
|
return str .. color.escapef(color.RED) .. "off" ..
|
|
|
|
color.escapef(color.WHITE);
|
2018-02-12 15:32:00 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-02-17 05:52:25 +00:00
|
|
|
return menu;
|