xref: /freebsd/stand/lua/menu.lua (revision fd2b19b3eeada3bb85075809899d71ec34152d3c)
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
44280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome
45088b4f5fSWarner Losh
46088b4f5fSWarner Loshmenu.boot_options = {
47d8757746SKyle Evans	entries = {
48088b4f5fSWarner Losh		-- return to welcome menu
49088b4f5fSWarner Losh		{
50a7cf0562SKyle Evans			entry_type = core.MENU_RETURN,
51088b4f5fSWarner Losh			name = function()
5257099121SKyle Evans				return "Back to main menu" ..
5357099121SKyle Evans				    color.highlight(" [Backspace]");
541666dfc0SKyle Evans			end
55088b4f5fSWarner Losh		},
56088b4f5fSWarner Losh
57088b4f5fSWarner Losh		-- load defaults
58088b4f5fSWarner Losh		{
59a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
60088b4f5fSWarner Losh			name = function()
6157099121SKyle Evans				return "Load System " .. color.highlight("D") ..
6257099121SKyle Evans				    "efaults";
63088b4f5fSWarner Losh			end,
64088b4f5fSWarner Losh			func = function()
6524a1bd54SKyle Evans				core.setDefaults();
66088b4f5fSWarner Losh			end,
67088b4f5fSWarner Losh			alias = {"d", "D"}
68088b4f5fSWarner Losh		},
69088b4f5fSWarner Losh
70088b4f5fSWarner Losh		{
71a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
72088b4f5fSWarner Losh			name = function()
73088b4f5fSWarner Losh				return "";
74088b4f5fSWarner Losh			end
75088b4f5fSWarner Losh		},
76088b4f5fSWarner Losh
77088b4f5fSWarner Losh		{
78a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
79088b4f5fSWarner Losh			name = function()
80088b4f5fSWarner Losh				return "Boot Options:";
81088b4f5fSWarner Losh			end
82088b4f5fSWarner Losh		},
83088b4f5fSWarner Losh
84088b4f5fSWarner Losh		-- acpi
85088b4f5fSWarner Losh		{
86a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
87088b4f5fSWarner Losh			name = function()
88*fd2b19b3SKyle Evans				return OnOff(color.highlight("A") ..
89*fd2b19b3SKyle Evans				    "CPI       :", core.acpi);
90088b4f5fSWarner Losh			end,
91088b4f5fSWarner Losh			func = function()
92088b4f5fSWarner Losh				core.setACPI();
93088b4f5fSWarner Losh			end,
94088b4f5fSWarner Losh			alias = {"a", "A"}
95088b4f5fSWarner Losh		},
96088b4f5fSWarner Losh		-- safe mode
97088b4f5fSWarner Losh		{
98a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
99088b4f5fSWarner Losh			name = function()
10057099121SKyle Evans				return OnOff("Safe " .. color.highlight("M") ..
10157099121SKyle Evans				    "ode  :", core.sm);
102088b4f5fSWarner Losh			end,
103088b4f5fSWarner Losh			func = function()
104088b4f5fSWarner Losh				core.setSafeMode();
105088b4f5fSWarner Losh			end,
106088b4f5fSWarner Losh			alias = {"m", "M"}
107088b4f5fSWarner Losh		},
108088b4f5fSWarner Losh		-- single user
109088b4f5fSWarner Losh		{
110a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
111088b4f5fSWarner Losh			name = function()
112*fd2b19b3SKyle Evans				return OnOff(color.highlight("S") ..
113*fd2b19b3SKyle Evans				    "ingle user:", core.su);
114088b4f5fSWarner Losh			end,
115088b4f5fSWarner Losh			func = function()
116088b4f5fSWarner Losh				core.setSingleUser();
117088b4f5fSWarner Losh			end,
118088b4f5fSWarner Losh			alias = {"s", "S"}
119088b4f5fSWarner Losh		},
120088b4f5fSWarner Losh		-- verbose boot
121088b4f5fSWarner Losh		{
122a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
123088b4f5fSWarner Losh			name = function()
124*fd2b19b3SKyle Evans				return OnOff(color.highlight("V") ..
125*fd2b19b3SKyle Evans				    "erbose    :", core.verbose);
126088b4f5fSWarner Losh			end,
127088b4f5fSWarner Losh			func = function()
128088b4f5fSWarner Losh				core.setVerbose();
129088b4f5fSWarner Losh			end,
130088b4f5fSWarner Losh			alias = {"v", "V"}
131088b4f5fSWarner Losh		},
132d8757746SKyle Evans	},
133088b4f5fSWarner Losh};
134088b4f5fSWarner Losh
135088b4f5fSWarner Loshmenu.welcome = {
136303253e5SKyle Evans	entries = function()
137303253e5SKyle Evans		local menu_entries = menu.welcome.all_entries;
138303253e5SKyle Evans		-- Swap the first two menu items on single user boot
139303253e5SKyle Evans		if (core.isSingleUserBoot()) then
140303253e5SKyle Evans			local multiuser = menu_entries[1];
141303253e5SKyle Evans			local singleuser = menu_entries[2];
142303253e5SKyle Evans
143303253e5SKyle Evans			menu_entries[2] = multiuser;
144303253e5SKyle Evans			menu_entries[1] = singleuser;
145303253e5SKyle Evans		end
146303253e5SKyle Evans		return menu_entries;
147303253e5SKyle Evans	end,
148303253e5SKyle Evans	all_entries = {
149088b4f5fSWarner Losh		-- boot multi user
150088b4f5fSWarner Losh		{
151a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
152088b4f5fSWarner Losh			name = function()
153*fd2b19b3SKyle Evans				return color.highlight("B") ..
154*fd2b19b3SKyle Evans				    "oot Multi user " ..
15557099121SKyle Evans				    color.highlight("[Enter]");
156088b4f5fSWarner Losh			end,
157088b4f5fSWarner Losh			func = function()
158088b4f5fSWarner Losh				core.setSingleUser(false);
159088b4f5fSWarner Losh				core.boot();
160088b4f5fSWarner Losh			end,
161b458bf0dSKyle Evans			alias = {"b", "B"}
162088b4f5fSWarner Losh		},
163088b4f5fSWarner Losh
164088b4f5fSWarner Losh		-- boot single user
165088b4f5fSWarner Losh		{
166a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
167088b4f5fSWarner Losh			name = function()
168*fd2b19b3SKyle Evans				return "Boot " .. color.highlight("S") ..
169*fd2b19b3SKyle Evans				    "ingle user";
170088b4f5fSWarner Losh			end,
171088b4f5fSWarner Losh			func = function()
172088b4f5fSWarner Losh				core.setSingleUser(true);
173088b4f5fSWarner Losh				core.boot();
174088b4f5fSWarner Losh			end,
175088b4f5fSWarner Losh			alias = {"s", "S"}
176088b4f5fSWarner Losh		},
177088b4f5fSWarner Losh
178088b4f5fSWarner Losh		-- escape to interpreter
179088b4f5fSWarner Losh		{
180a7cf0562SKyle Evans			entry_type = core.MENU_RETURN,
181088b4f5fSWarner Losh			name = function()
182*fd2b19b3SKyle Evans				return color.highlight("Esc") ..
183*fd2b19b3SKyle Evans				    "ape to loader prompt";
184088b4f5fSWarner Losh			end,
185ef625845SKyle Evans			func = function()
18624a1bd54SKyle Evans				loader.setenv("autoboot_delay", "NO");
187ef625845SKyle Evans			end,
18839006570SKyle Evans			alias = {core.KEYSTR_ESCAPE}
189088b4f5fSWarner Losh		},
190088b4f5fSWarner Losh
191088b4f5fSWarner Losh		-- reboot
192088b4f5fSWarner Losh		{
193a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
194088b4f5fSWarner Losh			name = function()
195088b4f5fSWarner Losh				return color.highlight("R") .. "eboot";
196088b4f5fSWarner Losh			end,
197088b4f5fSWarner Losh			func = function()
198088b4f5fSWarner Losh				loader.perform("reboot");
199088b4f5fSWarner Losh			end,
200088b4f5fSWarner Losh			alias = {"r", "R"}
201088b4f5fSWarner Losh		},
202088b4f5fSWarner Losh
203088b4f5fSWarner Losh
204088b4f5fSWarner Losh		{
205a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
206088b4f5fSWarner Losh			name = function()
207088b4f5fSWarner Losh				return "";
208088b4f5fSWarner Losh			end
209088b4f5fSWarner Losh		},
210088b4f5fSWarner Losh
211088b4f5fSWarner Losh		{
212a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
213088b4f5fSWarner Losh			name = function()
214088b4f5fSWarner Losh				return "Options:";
215088b4f5fSWarner Losh			end
216088b4f5fSWarner Losh		},
217088b4f5fSWarner Losh
218088b4f5fSWarner Losh		-- kernel options
219088b4f5fSWarner Losh		{
220a7cf0562SKyle Evans			entry_type = core.MENU_CAROUSEL_ENTRY,
221ada26c4aSKyle Evans			carousel_id = "kernel",
222ada26c4aSKyle Evans			items = core.kernelList,
223ada26c4aSKyle Evans			name = function(idx, choice, all_choices)
22424a1bd54SKyle Evans				if (#all_choices == 0) then
225b1b1f2b8SKyle Evans					return "Kernel: ";
226088b4f5fSWarner Losh				end
227b1b1f2b8SKyle Evans
228bcf48a15SKyle Evans				local is_default = (idx == 1);
229bcf48a15SKyle Evans				local kernel_name = "";
230bcf48a15SKyle Evans				local name_color;
23124a1bd54SKyle Evans				if (is_default) then
232bcf48a15SKyle Evans					name_color = color.escapef(color.GREEN);
233bcf48a15SKyle Evans					kernel_name = "default/";
234bcf48a15SKyle Evans				else
235bcf48a15SKyle Evans					name_color = color.escapef(color.BLUE);
236b1b1f2b8SKyle Evans				end
237*fd2b19b3SKyle Evans				kernel_name = kernel_name .. name_color ..
238*fd2b19b3SKyle Evans				    choice .. color.default();
239*fd2b19b3SKyle Evans				return color.highlight("K").."ernel: " ..
240*fd2b19b3SKyle Evans				    kernel_name .. " (" .. idx .. " of " ..
241*fd2b19b3SKyle Evans				    #all_choices .. ")";
242088b4f5fSWarner Losh			end,
2435d1e2f83SKyle Evans			func = function(idx, choice, all_choices)
244fa4a2394SKyle Evans				config.selectkernel(choice);
245088b4f5fSWarner Losh			end,
246088b4f5fSWarner Losh			alias = {"k", "K"}
247088b4f5fSWarner Losh		},
248088b4f5fSWarner Losh
249088b4f5fSWarner Losh		-- boot options
250088b4f5fSWarner Losh		{
251a7cf0562SKyle Evans			entry_type = core.MENU_SUBMENU,
252088b4f5fSWarner Losh			name = function()
253*fd2b19b3SKyle Evans				return "Boot " .. color.highlight("O") ..
254*fd2b19b3SKyle Evans				    "ptions";
255088b4f5fSWarner Losh			end,
256088b4f5fSWarner Losh			submenu = function()
257088b4f5fSWarner Losh				return menu.boot_options;
258088b4f5fSWarner Losh			end,
259088b4f5fSWarner Losh			alias = {"o", "O"}
260d8757746SKyle Evans		},
261d8757746SKyle Evans	},
262088b4f5fSWarner Losh};
263088b4f5fSWarner Losh
264ada26c4aSKyle Evans-- The first item in every carousel is always the default item.
265ada26c4aSKyle Evansfunction menu.getCarouselIndex(id)
266ada26c4aSKyle Evans	local val = carousel_choices[id];
267ada26c4aSKyle Evans	if (val == nil) then
268ada26c4aSKyle Evans		return 1;
269ada26c4aSKyle Evans	end
270ada26c4aSKyle Evans	return val;
271ada26c4aSKyle Evansend
272ada26c4aSKyle Evans
273ada26c4aSKyle Evansfunction menu.setCarouselIndex(id, idx)
274ada26c4aSKyle Evans	carousel_choices[id] = idx;
275ada26c4aSKyle Evansend
276ada26c4aSKyle Evans
277088b4f5fSWarner Loshfunction menu.run(m)
278088b4f5fSWarner Losh
279088b4f5fSWarner Losh	if (menu.skip()) then
280088b4f5fSWarner Losh		core.autoboot();
281088b4f5fSWarner Losh		return false;
282088b4f5fSWarner Losh	end
283088b4f5fSWarner Losh
284088b4f5fSWarner Losh	if (m == nil) then
285088b4f5fSWarner Losh		m = menu.welcome;
286088b4f5fSWarner Losh	end
287088b4f5fSWarner Losh
288088b4f5fSWarner Losh	-- redraw screen
289088b4f5fSWarner Losh	screen.clear();
290088b4f5fSWarner Losh	screen.defcursor();
291088b4f5fSWarner Losh	local alias_table = drawer.drawscreen(m);
292088b4f5fSWarner Losh
2933a0a07d0SKyle Evans	menu.autoboot();
294088b4f5fSWarner Losh
295088b4f5fSWarner Losh	cont = true;
29624a1bd54SKyle Evans	while (cont) do
297abc4f7e7SKyle Evans		local key = io.getchar();
298088b4f5fSWarner Losh
299b458bf0dSKyle Evans		-- Special key behaviors
3001504bce3SKyle Evans		if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
3011504bce3SKyle Evans		    (m ~= menu.welcome) then
302abc4f7e7SKyle Evans			break
303fe672a15SKyle Evans		elseif (key == core.KEY_ENTER) then
304b458bf0dSKyle Evans			core.boot();
305b458bf0dSKyle Evans			-- Should not return
306abc4f7e7SKyle Evans		end
307abc4f7e7SKyle Evans
308abc4f7e7SKyle Evans		key = string.char(key)
309088b4f5fSWarner Losh		-- check to see if key is an alias
310088b4f5fSWarner Losh		local sel_entry = nil;
311088b4f5fSWarner Losh		for k, v in pairs(alias_table) do
312088b4f5fSWarner Losh			if (key == k) then
313088b4f5fSWarner Losh				sel_entry = v;
314088b4f5fSWarner Losh			end
315088b4f5fSWarner Losh		end
316088b4f5fSWarner Losh
317088b4f5fSWarner Losh		-- if we have an alias do the assigned action:
318088b4f5fSWarner Losh		if (sel_entry ~= nil) then
319a7cf0562SKyle Evans			if (sel_entry.entry_type == core.MENU_ENTRY) then
320088b4f5fSWarner Losh				-- run function
321088b4f5fSWarner Losh				sel_entry.func();
322a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_CAROUSEL_ENTRY) then
323ada26c4aSKyle Evans				-- carousel (rotating) functionality
324ada26c4aSKyle Evans				local carid = sel_entry.carousel_id;
325ada26c4aSKyle Evans				local caridx = menu.getCarouselIndex(carid);
326ada26c4aSKyle Evans				local choices = sel_entry.items();
327ada26c4aSKyle Evans
328aefcaa7eSKyle Evans				if (#choices > 0) then
329ada26c4aSKyle Evans					caridx = (caridx % #choices) + 1;
330ada26c4aSKyle Evans					menu.setCarouselIndex(carid, caridx);
3315d1e2f83SKyle Evans					sel_entry.func(caridx, choices[caridx],
33284f82e46SKyle Evans					    choices);
333aefcaa7eSKyle Evans				end
334a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_SUBMENU) then
335088b4f5fSWarner Losh				-- recurse
336088b4f5fSWarner Losh				cont = menu.run(sel_entry.submenu());
337a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_RETURN) then
338ef625845SKyle Evans				-- allow entry to have a function/side effect
339ef625845SKyle Evans				if (sel_entry.func ~= nil) then
340ef625845SKyle Evans					sel_entry.func();
341ef625845SKyle Evans				end
342088b4f5fSWarner Losh				-- break recurse
343088b4f5fSWarner Losh				cont = false;
344088b4f5fSWarner Losh			end
345088b4f5fSWarner Losh			-- if we got an alias key the screen is out of date:
346088b4f5fSWarner Losh			screen.clear();
347088b4f5fSWarner Losh			screen.defcursor();
348088b4f5fSWarner Losh			alias_table = drawer.drawscreen(m);
349088b4f5fSWarner Losh		end
350088b4f5fSWarner Losh	end
351088b4f5fSWarner Losh
352088b4f5fSWarner Losh	if (m == menu.welcome) then
353088b4f5fSWarner Losh		screen.defcursor();
354088b4f5fSWarner Losh		print("Exiting menu!");
355fa4a2394SKyle Evans		config.loadelf();
356088b4f5fSWarner Losh		return false;
357088b4f5fSWarner Losh	end
358088b4f5fSWarner Losh
359088b4f5fSWarner Losh	return true;
360088b4f5fSWarner Loshend
361088b4f5fSWarner Losh
362088b4f5fSWarner Loshfunction menu.skip()
363b140d14bSKyle Evans	if (core.isSerialBoot()) then
364088b4f5fSWarner Losh		return true;
365088b4f5fSWarner Losh	end
366088b4f5fSWarner Losh	local c = string.lower(loader.getenv("console") or "");
36724a1bd54SKyle Evans	if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
368088b4f5fSWarner Losh		return true;
369088b4f5fSWarner Losh	end
370088b4f5fSWarner Losh
371088b4f5fSWarner Losh	c = string.lower(loader.getenv("beastie_disable") or "");
372088b4f5fSWarner Losh	print("beastie_disable", c);
373088b4f5fSWarner Losh	return c == "yes";
374088b4f5fSWarner Loshend
375088b4f5fSWarner Losh
376088b4f5fSWarner Loshfunction menu.autoboot()
37724a1bd54SKyle Evans	if (menu.already_autoboot == true) then
378088b4f5fSWarner Losh		return;
379088b4f5fSWarner Losh	end
380088b4f5fSWarner Losh	menu.already_autoboot = true;
381088b4f5fSWarner Losh
382088b4f5fSWarner Losh	local ab = loader.getenv("autoboot_delay");
383702b460dSKyle Evans	if (ab ~= nil) and (ab:lower() == "no") then
384702b460dSKyle Evans		return;
385702b460dSKyle Evans	elseif (tonumber(ab) == -1) then
386088b4f5fSWarner Losh		core.boot();
387088b4f5fSWarner Losh	end
388088b4f5fSWarner Losh	ab = tonumber(ab) or 10;
389088b4f5fSWarner Losh
390088b4f5fSWarner Losh	local x = loader.getenv("loader_menu_timeout_x") or 5;
391088b4f5fSWarner Losh	local y = loader.getenv("loader_menu_timeout_y") or 22;
392088b4f5fSWarner Losh
393088b4f5fSWarner Losh	local endtime = loader.time() + ab;
394088b4f5fSWarner Losh	local time;
395088b4f5fSWarner Losh
396088b4f5fSWarner Losh	repeat
397088b4f5fSWarner Losh		time = endtime - loader.time();
398088b4f5fSWarner Losh		screen.setcursor(x, y);
39957099121SKyle Evans		print("Autoboot in " .. time ..
40057099121SKyle Evans		    " seconds, hit [Enter] to boot" ..
40157099121SKyle Evans		    " or any other key to stop     ");
402088b4f5fSWarner Losh		screen.defcursor();
40324a1bd54SKyle Evans		if (io.ischar()) then
404088b4f5fSWarner Losh			local ch = io.getchar();
40524a1bd54SKyle Evans			if (ch == core.KEY_ENTER) then
406088b4f5fSWarner Losh				break;
407088b4f5fSWarner Losh			else
408088b4f5fSWarner Losh				-- erase autoboot msg
409088b4f5fSWarner Losh				screen.setcursor(0, y);
410088b4f5fSWarner Losh				print("                                        "
411088b4f5fSWarner Losh				    .. "                                        ");
412088b4f5fSWarner Losh				screen.defcursor();
413088b4f5fSWarner Losh				return;
414088b4f5fSWarner Losh			end
415088b4f5fSWarner Losh		end
416088b4f5fSWarner Losh
417088b4f5fSWarner Losh		loader.delay(50000);
418088b4f5fSWarner Losh	until time <= 0
419088b4f5fSWarner Losh	core.boot();
420088b4f5fSWarner Losh
421088b4f5fSWarner Loshend
422088b4f5fSWarner Losh
423088b4f5fSWarner Loshfunction OnOff(str, b)
424088b4f5fSWarner Losh	if (b) then
42557099121SKyle Evans		return str .. color.escapef(color.GREEN) .. "On" ..
42657099121SKyle Evans		    color.escapef(color.WHITE);
427088b4f5fSWarner Losh	else
42857099121SKyle Evans		return str .. color.escapef(color.RED) .. "off" ..
42957099121SKyle Evans		    color.escapef(color.WHITE);
430088b4f5fSWarner Losh	end
431088b4f5fSWarner Loshend
432088b4f5fSWarner Losh
43324a1bd54SKyle Evansreturn menu;
434