xref: /freebsd/stand/lua/menu.lua (revision abc4f7e735da8c11c390ef742d5fa9823db54168)
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