xref: /freebsd/stand/lua/menu.lua (revision 280e990ba78446e0a04c845abf70731748b6281d)
1088b4f5fSWarner Losh--
2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3088b4f5fSWarner Losh-- All rights reserved.
4088b4f5fSWarner Losh--
5088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
6088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
7088b4f5fSWarner Losh-- are met:
8088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
9088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
10088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
11088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
12088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
13088b4f5fSWarner Losh--
14088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24088b4f5fSWarner Losh-- SUCH DAMAGE.
25088b4f5fSWarner Losh--
26088b4f5fSWarner Losh-- $FreeBSD$
27088b4f5fSWarner Losh--
28088b4f5fSWarner Losh
29088b4f5fSWarner Losh
30088b4f5fSWarner Loshlocal menu = {};
31088b4f5fSWarner Losh
32088b4f5fSWarner Loshlocal core = require("core");
33088b4f5fSWarner Loshlocal color = require("color");
34088b4f5fSWarner Loshlocal config = require("config");
35088b4f5fSWarner Loshlocal screen = require("screen");
36088b4f5fSWarner Loshlocal drawer = require("drawer");
37088b4f5fSWarner Losh
38088b4f5fSWarner Loshlocal OnOff;
39088b4f5fSWarner Loshlocal skip;
40088b4f5fSWarner Loshlocal run;
41088b4f5fSWarner Loshlocal autoboot;
42ada26c4aSKyle Evanslocal carousel_choices = {};
43088b4f5fSWarner Losh
44*280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome
45088b4f5fSWarner Losh
46088b4f5fSWarner Loshmenu.boot_options = {
47088b4f5fSWarner Losh	-- return to welcome menu
48088b4f5fSWarner Losh	{
49a7cf0562SKyle Evans		entry_type = core.MENU_RETURN,
50088b4f5fSWarner Losh		name = function()
5157099121SKyle Evans			return "Back to main menu" ..
5257099121SKyle Evans			    color.highlight(" [Backspace]");
531666dfc0SKyle Evans		end
54088b4f5fSWarner Losh	},
55088b4f5fSWarner Losh
56088b4f5fSWarner Losh	-- load defaults
57088b4f5fSWarner Losh	{
58a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
59088b4f5fSWarner Losh		name = function()
6057099121SKyle Evans			return "Load System " .. color.highlight("D") ..
6157099121SKyle Evans			    "efaults";
62088b4f5fSWarner Losh		end,
63088b4f5fSWarner Losh		func = function()
6424a1bd54SKyle Evans			core.setDefaults();
65088b4f5fSWarner Losh		end,
66088b4f5fSWarner Losh		alias = {"d", "D"}
67088b4f5fSWarner Losh	},
68088b4f5fSWarner Losh
69088b4f5fSWarner Losh	{
70a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
71088b4f5fSWarner Losh		name = function()
72088b4f5fSWarner Losh			return "";
73088b4f5fSWarner Losh		end
74088b4f5fSWarner Losh	},
75088b4f5fSWarner Losh
76088b4f5fSWarner Losh	{
77a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
78088b4f5fSWarner Losh		name = function()
79088b4f5fSWarner Losh			return "Boot Options:";
80088b4f5fSWarner Losh		end
81088b4f5fSWarner Losh	},
82088b4f5fSWarner Losh
83088b4f5fSWarner Losh	-- acpi
84088b4f5fSWarner Losh	{
85a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
86088b4f5fSWarner Losh		name = function()
8757099121SKyle Evans			return OnOff(color.highlight("A") .. "CPI       :",
8857099121SKyle Evans			    core.acpi);
89088b4f5fSWarner Losh		end,
90088b4f5fSWarner Losh		func = function()
91088b4f5fSWarner Losh			core.setACPI();
92088b4f5fSWarner Losh		end,
93088b4f5fSWarner Losh		alias = {"a", "A"}
94088b4f5fSWarner Losh	},
95088b4f5fSWarner Losh	-- safe mode
96088b4f5fSWarner Losh	{
97a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
98088b4f5fSWarner Losh		name = function()
9957099121SKyle Evans			return OnOff("Safe " .. color.highlight("M") ..
10057099121SKyle Evans			    "ode  :", core.sm);
101088b4f5fSWarner Losh		end,
102088b4f5fSWarner Losh		func = function()
103088b4f5fSWarner Losh			core.setSafeMode();
104088b4f5fSWarner Losh		end,
105088b4f5fSWarner Losh		alias = {"m", "M"}
106088b4f5fSWarner Losh	},
107088b4f5fSWarner Losh	-- single user
108088b4f5fSWarner Losh	{
109a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
110088b4f5fSWarner Losh		name = function()
11157099121SKyle Evans			return OnOff(color.highlight("S") .. "ingle user:",
11257099121SKyle Evans			    core.su);
113088b4f5fSWarner Losh		end,
114088b4f5fSWarner Losh		func = function()
115088b4f5fSWarner Losh			core.setSingleUser();
116088b4f5fSWarner Losh		end,
117088b4f5fSWarner Losh		alias = {"s", "S"}
118088b4f5fSWarner Losh	},
119088b4f5fSWarner Losh	-- verbose boot
120088b4f5fSWarner Losh	{
121a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
122088b4f5fSWarner Losh		name = function()
12357099121SKyle Evans			return OnOff(color.highlight("V") .. "erbose    :",
12457099121SKyle Evans			    core.verbose);
125088b4f5fSWarner Losh		end,
126088b4f5fSWarner Losh		func = function()
127088b4f5fSWarner Losh			core.setVerbose();
128088b4f5fSWarner Losh		end,
129088b4f5fSWarner Losh		alias = {"v", "V"}
130088b4f5fSWarner Losh	},
131088b4f5fSWarner Losh};
132088b4f5fSWarner Losh
133088b4f5fSWarner Loshmenu.welcome = {
134088b4f5fSWarner Losh	-- boot multi user
135088b4f5fSWarner Losh	{
136a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
137088b4f5fSWarner Losh		name = function()
13857099121SKyle Evans			return color.highlight("B") .. "oot Multi user " ..
13957099121SKyle Evans			    color.highlight("[Enter]");
140088b4f5fSWarner Losh		end,
141088b4f5fSWarner Losh		func = function()
142088b4f5fSWarner Losh			core.setSingleUser(false);
143088b4f5fSWarner Losh			core.boot();
144088b4f5fSWarner Losh		end,
145b458bf0dSKyle Evans		alias = {"b", "B"}
146088b4f5fSWarner Losh	},
147088b4f5fSWarner Losh
148088b4f5fSWarner Losh	-- boot single user
149088b4f5fSWarner Losh	{
150a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
151088b4f5fSWarner Losh		name = function()
152088b4f5fSWarner Losh			return "Boot " .. color.highlight("S") .. "ingle user";
153088b4f5fSWarner Losh		end,
154088b4f5fSWarner Losh		func = function()
155088b4f5fSWarner Losh			core.setSingleUser(true);
156088b4f5fSWarner Losh			core.boot();
157088b4f5fSWarner Losh		end,
158088b4f5fSWarner Losh		alias = {"s", "S"}
159088b4f5fSWarner Losh	},
160088b4f5fSWarner Losh
161088b4f5fSWarner Losh	-- escape to interpreter
162088b4f5fSWarner Losh	{
163a7cf0562SKyle Evans		entry_type = core.MENU_RETURN,
164088b4f5fSWarner Losh		name = function()
165e9084012SKyle Evans			return color.highlight("Esc") .. "ape to loader prompt";
166088b4f5fSWarner Losh		end,
167ef625845SKyle Evans		func = function()
16824a1bd54SKyle Evans			loader.setenv("autoboot_delay", "NO");
169ef625845SKyle Evans		end,
17039006570SKyle Evans		alias = {core.KEYSTR_ESCAPE}
171088b4f5fSWarner Losh	},
172088b4f5fSWarner Losh
173088b4f5fSWarner Losh	-- reboot
174088b4f5fSWarner Losh	{
175a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
176088b4f5fSWarner Losh		name = function()
177088b4f5fSWarner Losh			return color.highlight("R") .. "eboot";
178088b4f5fSWarner Losh		end,
179088b4f5fSWarner Losh		func = function()
180088b4f5fSWarner Losh			loader.perform("reboot");
181088b4f5fSWarner Losh		end,
182088b4f5fSWarner Losh		alias = {"r", "R"}
183088b4f5fSWarner Losh	},
184088b4f5fSWarner Losh
185088b4f5fSWarner Losh
186088b4f5fSWarner Losh	{
187a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
188088b4f5fSWarner Losh		name = function()
189088b4f5fSWarner Losh			return "";
190088b4f5fSWarner Losh		end
191088b4f5fSWarner Losh	},
192088b4f5fSWarner Losh
193088b4f5fSWarner Losh	{
194a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
195088b4f5fSWarner Losh		name = function()
196088b4f5fSWarner Losh			return "Options:";
197088b4f5fSWarner Losh		end
198088b4f5fSWarner Losh	},
199088b4f5fSWarner Losh
200088b4f5fSWarner Losh	-- kernel options
201088b4f5fSWarner Losh	{
202a7cf0562SKyle Evans		entry_type = core.MENU_CAROUSEL_ENTRY,
203ada26c4aSKyle Evans		carousel_id = "kernel",
204ada26c4aSKyle Evans		items = core.kernelList,
205ada26c4aSKyle Evans		name = function(idx, choice, all_choices)
20624a1bd54SKyle Evans			if (#all_choices == 0) then
207b1b1f2b8SKyle Evans				return "Kernel: ";
208088b4f5fSWarner Losh			end
209b1b1f2b8SKyle Evans
210bcf48a15SKyle Evans			local is_default = (idx == 1);
211bcf48a15SKyle Evans			local kernel_name = "";
212bcf48a15SKyle Evans			local name_color;
21324a1bd54SKyle Evans			if (is_default) then
214bcf48a15SKyle Evans				name_color = color.escapef(color.GREEN);
215bcf48a15SKyle Evans				kernel_name = "default/";
216bcf48a15SKyle Evans			else
217bcf48a15SKyle Evans				name_color = color.escapef(color.BLUE);
218b1b1f2b8SKyle Evans			end
219bcf48a15SKyle Evans			kernel_name = kernel_name .. name_color .. choice ..
220bcf48a15SKyle Evans			    color.default();
221b1b1f2b8SKyle Evans			return color.highlight("K").."ernel: " .. kernel_name ..
222ada26c4aSKyle Evans			    " (" .. idx ..
223ada26c4aSKyle Evans			    " of " .. #all_choices .. ")";
224088b4f5fSWarner Losh		end,
2255d1e2f83SKyle Evans		func = function(idx, choice, all_choices)
226fa4a2394SKyle Evans			config.selectkernel(choice);
227088b4f5fSWarner Losh		end,
228088b4f5fSWarner Losh		alias = {"k", "K"}
229088b4f5fSWarner Losh	},
230088b4f5fSWarner Losh
231088b4f5fSWarner Losh	-- boot options
232088b4f5fSWarner Losh	{
233a7cf0562SKyle Evans		entry_type = core.MENU_SUBMENU,
234088b4f5fSWarner Losh		name = function()
235088b4f5fSWarner Losh			return "Boot " .. color.highlight("O") .. "ptions";
236088b4f5fSWarner Losh		end,
237088b4f5fSWarner Losh		submenu = function()
238088b4f5fSWarner Losh			return menu.boot_options;
239088b4f5fSWarner Losh		end,
240088b4f5fSWarner Losh		alias = {"o", "O"}
241088b4f5fSWarner Losh	}
242088b4f5fSWarner Losh
243088b4f5fSWarner Losh};
244088b4f5fSWarner Losh
245ada26c4aSKyle Evans-- The first item in every carousel is always the default item.
246ada26c4aSKyle Evansfunction menu.getCarouselIndex(id)
247ada26c4aSKyle Evans	local val = carousel_choices[id];
248ada26c4aSKyle Evans	if (val == nil) then
249ada26c4aSKyle Evans		return 1;
250ada26c4aSKyle Evans	end
251ada26c4aSKyle Evans	return val;
252ada26c4aSKyle Evansend
253ada26c4aSKyle Evans
254ada26c4aSKyle Evansfunction menu.setCarouselIndex(id, idx)
255ada26c4aSKyle Evans	carousel_choices[id] = idx;
256ada26c4aSKyle Evansend
257ada26c4aSKyle Evans
258088b4f5fSWarner Loshfunction menu.run(m)
259088b4f5fSWarner Losh
260088b4f5fSWarner Losh	if (menu.skip()) then
261088b4f5fSWarner Losh		core.autoboot();
262088b4f5fSWarner Losh		return false;
263088b4f5fSWarner Losh	end
264088b4f5fSWarner Losh
265088b4f5fSWarner Losh	if (m == nil) then
266088b4f5fSWarner Losh		m = menu.welcome;
267088b4f5fSWarner Losh	end
268088b4f5fSWarner Losh
269088b4f5fSWarner Losh	-- redraw screen
270088b4f5fSWarner Losh	screen.clear();
271088b4f5fSWarner Losh	screen.defcursor();
272088b4f5fSWarner Losh	local alias_table = drawer.drawscreen(m);
273088b4f5fSWarner Losh
2743a0a07d0SKyle Evans	menu.autoboot();
275088b4f5fSWarner Losh
276088b4f5fSWarner Losh	cont = true;
27724a1bd54SKyle Evans	while (cont) do
278abc4f7e7SKyle Evans		local key = io.getchar();
279088b4f5fSWarner Losh
280b458bf0dSKyle Evans		-- Special key behaviors
2811504bce3SKyle Evans		if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
2821504bce3SKyle Evans		    (m ~= menu.welcome) then
283abc4f7e7SKyle Evans			break
284fe672a15SKyle Evans		elseif (key == core.KEY_ENTER) then
285b458bf0dSKyle Evans			core.boot();
286b458bf0dSKyle Evans			-- Should not return
287abc4f7e7SKyle Evans		end
288abc4f7e7SKyle Evans
289abc4f7e7SKyle Evans		key = string.char(key)
290088b4f5fSWarner Losh		-- check to see if key is an alias
291088b4f5fSWarner Losh		local sel_entry = nil;
292088b4f5fSWarner Losh		for k, v in pairs(alias_table) do
293088b4f5fSWarner Losh			if (key == k) then
294088b4f5fSWarner Losh				sel_entry = v;
295088b4f5fSWarner Losh			end
296088b4f5fSWarner Losh		end
297088b4f5fSWarner Losh
298088b4f5fSWarner Losh		-- if we have an alias do the assigned action:
299088b4f5fSWarner Losh		if (sel_entry ~= nil) then
300a7cf0562SKyle Evans			if (sel_entry.entry_type == core.MENU_ENTRY) then
301088b4f5fSWarner Losh				-- run function
302088b4f5fSWarner Losh				sel_entry.func();
303a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_CAROUSEL_ENTRY) then
304ada26c4aSKyle Evans				-- carousel (rotating) functionality
305ada26c4aSKyle Evans				local carid = sel_entry.carousel_id;
306ada26c4aSKyle Evans				local caridx = menu.getCarouselIndex(carid);
307ada26c4aSKyle Evans				local choices = sel_entry.items();
308ada26c4aSKyle Evans
309aefcaa7eSKyle Evans				if (#choices > 0) then
310ada26c4aSKyle Evans					caridx = (caridx % #choices) + 1;
311ada26c4aSKyle Evans					menu.setCarouselIndex(carid, caridx);
3125d1e2f83SKyle Evans					sel_entry.func(caridx, choices[caridx],
31384f82e46SKyle Evans					    choices);
314aefcaa7eSKyle Evans				end
315a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_SUBMENU) then
316088b4f5fSWarner Losh				-- recurse
317088b4f5fSWarner Losh				cont = menu.run(sel_entry.submenu());
318a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_RETURN) then
319ef625845SKyle Evans				-- allow entry to have a function/side effect
320ef625845SKyle Evans				if (sel_entry.func ~= nil) then
321ef625845SKyle Evans					sel_entry.func();
322ef625845SKyle Evans				end
323088b4f5fSWarner Losh				-- break recurse
324088b4f5fSWarner Losh				cont = false;
325088b4f5fSWarner Losh			end
326088b4f5fSWarner Losh			-- if we got an alias key the screen is out of date:
327088b4f5fSWarner Losh			screen.clear();
328088b4f5fSWarner Losh			screen.defcursor();
329088b4f5fSWarner Losh			alias_table = drawer.drawscreen(m);
330088b4f5fSWarner Losh		end
331088b4f5fSWarner Losh	end
332088b4f5fSWarner Losh
333088b4f5fSWarner Losh	if (m == menu.welcome) then
334088b4f5fSWarner Losh		screen.defcursor();
335088b4f5fSWarner Losh		print("Exiting menu!");
336fa4a2394SKyle Evans		config.loadelf();
337088b4f5fSWarner Losh		return false;
338088b4f5fSWarner Losh	end
339088b4f5fSWarner Losh
340088b4f5fSWarner Losh	return true;
341088b4f5fSWarner Loshend
342088b4f5fSWarner Losh
343088b4f5fSWarner Loshfunction menu.skip()
344b140d14bSKyle Evans	if (core.isSerialBoot()) then
345088b4f5fSWarner Losh		return true;
346088b4f5fSWarner Losh	end
347088b4f5fSWarner Losh	local c = string.lower(loader.getenv("console") or "");
34824a1bd54SKyle Evans	if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
349088b4f5fSWarner Losh		return true;
350088b4f5fSWarner Losh	end
351088b4f5fSWarner Losh
352088b4f5fSWarner Losh	c = string.lower(loader.getenv("beastie_disable") or "");
353088b4f5fSWarner Losh	print("beastie_disable", c);
354088b4f5fSWarner Losh	return c == "yes";
355088b4f5fSWarner Loshend
356088b4f5fSWarner Losh
357088b4f5fSWarner Loshfunction menu.autoboot()
35824a1bd54SKyle Evans	if (menu.already_autoboot == true) then
359088b4f5fSWarner Losh		return;
360088b4f5fSWarner Losh	end
361088b4f5fSWarner Losh	menu.already_autoboot = true;
362088b4f5fSWarner Losh
363088b4f5fSWarner Losh	local ab = loader.getenv("autoboot_delay");
364702b460dSKyle Evans	if (ab ~= nil) and (ab:lower() == "no") then
365702b460dSKyle Evans		return;
366702b460dSKyle Evans	elseif (tonumber(ab) == -1) then
367088b4f5fSWarner Losh		core.boot();
368088b4f5fSWarner Losh	end
369088b4f5fSWarner Losh	ab = tonumber(ab) or 10;
370088b4f5fSWarner Losh
371088b4f5fSWarner Losh	local x = loader.getenv("loader_menu_timeout_x") or 5;
372088b4f5fSWarner Losh	local y = loader.getenv("loader_menu_timeout_y") or 22;
373088b4f5fSWarner Losh
374088b4f5fSWarner Losh	local endtime = loader.time() + ab;
375088b4f5fSWarner Losh	local time;
376088b4f5fSWarner Losh
377088b4f5fSWarner Losh	repeat
378088b4f5fSWarner Losh		time = endtime - loader.time();
379088b4f5fSWarner Losh		screen.setcursor(x, y);
38057099121SKyle Evans		print("Autoboot in " .. time ..
38157099121SKyle Evans		    " seconds, hit [Enter] to boot" ..
38257099121SKyle Evans		    " or any other key to stop     ");
383088b4f5fSWarner Losh		screen.defcursor();
38424a1bd54SKyle Evans		if (io.ischar()) then
385088b4f5fSWarner Losh			local ch = io.getchar();
38624a1bd54SKyle Evans			if (ch == core.KEY_ENTER) then
387088b4f5fSWarner Losh				break;
388088b4f5fSWarner Losh			else
389088b4f5fSWarner Losh				-- erase autoboot msg
390088b4f5fSWarner Losh				screen.setcursor(0, y);
391088b4f5fSWarner Losh				print("                                        "
392088b4f5fSWarner Losh				    .. "                                        ");
393088b4f5fSWarner Losh				screen.defcursor();
394088b4f5fSWarner Losh				return;
395088b4f5fSWarner Losh			end
396088b4f5fSWarner Losh		end
397088b4f5fSWarner Losh
398088b4f5fSWarner Losh		loader.delay(50000);
399088b4f5fSWarner Losh	until time <= 0
400088b4f5fSWarner Losh	core.boot();
401088b4f5fSWarner Losh
402088b4f5fSWarner Loshend
403088b4f5fSWarner Losh
404088b4f5fSWarner Loshfunction OnOff(str, b)
405088b4f5fSWarner Losh	if (b) then
40657099121SKyle Evans		return str .. color.escapef(color.GREEN) .. "On" ..
40757099121SKyle Evans		    color.escapef(color.WHITE);
408088b4f5fSWarner Losh	else
40957099121SKyle Evans		return str .. color.escapef(color.RED) .. "off" ..
41057099121SKyle Evans		    color.escapef(color.WHITE);
411088b4f5fSWarner Losh	end
412088b4f5fSWarner Loshend
413088b4f5fSWarner Losh
41424a1bd54SKyle Evansreturn menu;
415