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