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