xref: /freebsd/stand/lua/menu.lua (revision a51f9f0c480fc3d10f5938c91ea0a1dd82c10fb1)
1088b4f5fSWarner Losh--
2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
321d5bcbeSKyle Evans-- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
4088b4f5fSWarner Losh-- All rights reserved.
5088b4f5fSWarner Losh--
6088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
7088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
8088b4f5fSWarner Losh-- are met:
9088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
10088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
11088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
12088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
13088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
14088b4f5fSWarner Losh--
15088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25088b4f5fSWarner Losh-- SUCH DAMAGE.
26088b4f5fSWarner Losh--
27088b4f5fSWarner Losh-- $FreeBSD$
28088b4f5fSWarner Losh--
29088b4f5fSWarner Losh
30088b4f5fSWarner Losh
31aedd6be5SKyle Evanslocal core = require("core")
32aedd6be5SKyle Evanslocal color = require("color")
33aedd6be5SKyle Evanslocal config = require("config")
34aedd6be5SKyle Evanslocal screen = require("screen")
35aedd6be5SKyle Evanslocal drawer = require("drawer")
36088b4f5fSWarner Losh
37aedd6be5SKyle Evanslocal menu = {}
38c8518398SKyle Evans
39aedd6be5SKyle Evanslocal skip
40aedd6be5SKyle Evanslocal run
41aedd6be5SKyle Evanslocal autoboot
42088b4f5fSWarner Losh
43e15abd1fSKyle Evanslocal OnOff = function(str, b)
449f71d421SKyle Evans	if b then
45e15abd1fSKyle Evans		return str .. color.escapef(color.GREEN) .. "On" ..
46aedd6be5SKyle Evans		    color.escapef(color.WHITE)
47e15abd1fSKyle Evans	else
48e15abd1fSKyle Evans		return str .. color.escapef(color.RED) .. "off" ..
49aedd6be5SKyle Evans		    color.escapef(color.WHITE)
50e15abd1fSKyle Evans	end
51e15abd1fSKyle Evansend
52e15abd1fSKyle Evans
53b5746545SKyle Evans-- Module exports
548d415029SKyle Evansmenu.handlers = {
558d415029SKyle Evans	-- Menu handlers take the current menu and selected entry as parameters,
568d415029SKyle Evans	-- and should return a boolean indicating whether execution should
578d415029SKyle Evans	-- continue or not. The return value may be omitted if this entry should
588d415029SKyle Evans	-- have no bearing on whether we continue or not, indicating that we
598d415029SKyle Evans	-- should just continue after execution.
608d415029SKyle Evans	[core.MENU_ENTRY] = function(current_menu, entry)
618d415029SKyle Evans		-- run function
62aedd6be5SKyle Evans		entry.func()
638d415029SKyle Evans	end,
648d415029SKyle Evans	[core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry)
658d415029SKyle Evans		-- carousel (rotating) functionality
66aedd6be5SKyle Evans		local carid = entry.carousel_id
67aedd6be5SKyle Evans		local caridx = config.getCarouselIndex(carid)
68aedd6be5SKyle Evans		local choices = entry.items()
698d415029SKyle Evans
709f71d421SKyle Evans		if #choices > 0 then
71aedd6be5SKyle Evans			caridx = (caridx % #choices) + 1
72aedd6be5SKyle Evans			config.setCarouselIndex(carid, caridx)
73aedd6be5SKyle Evans			entry.func(caridx, choices[caridx], choices)
748d415029SKyle Evans		end
758d415029SKyle Evans	end,
768d415029SKyle Evans	[core.MENU_SUBMENU] = function(current_menu, entry)
778d415029SKyle Evans		-- recurse
789a28f948SKyle Evans		return menu.run(entry.submenu)
798d415029SKyle Evans	end,
808d415029SKyle Evans	[core.MENU_RETURN] = function(current_menu, entry)
818d415029SKyle Evans		-- allow entry to have a function/side effect
829f71d421SKyle Evans		if entry.func ~= nil then
83aedd6be5SKyle Evans			entry.func()
848d415029SKyle Evans		end
85aedd6be5SKyle Evans		return false
868d415029SKyle Evans	end,
87aedd6be5SKyle Evans}
88280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome
89088b4f5fSWarner Losh
90088b4f5fSWarner Loshmenu.boot_options = {
91d8757746SKyle Evans	entries = {
92088b4f5fSWarner Losh		-- return to welcome menu
93088b4f5fSWarner Losh		{
94a7cf0562SKyle Evans			entry_type = core.MENU_RETURN,
95*a51f9f0cSKyle Evans			name = "Back to main menu" ..
96*a51f9f0cSKyle Evans			    color.highlight(" [Backspace]"),
97088b4f5fSWarner Losh		},
98088b4f5fSWarner Losh		-- load defaults
99088b4f5fSWarner Losh		{
100a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
101*a51f9f0cSKyle Evans			name = "Load System " .. color.highlight("D") ..
102*a51f9f0cSKyle Evans			    "efaults",
103*a51f9f0cSKyle Evans			func = core.setDefaults,
104088b4f5fSWarner Losh			alias = {"d", "D"}
105088b4f5fSWarner Losh		},
106088b4f5fSWarner Losh		{
107a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
108088b4f5fSWarner Losh		},
109088b4f5fSWarner Losh		{
110a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
111*a51f9f0cSKyle Evans			name = "Boot Options:",
112088b4f5fSWarner Losh		},
113088b4f5fSWarner Losh		-- acpi
114088b4f5fSWarner Losh		{
115a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
116c1ab36f5SKyle Evans			visible = core.isSystem386,
117088b4f5fSWarner Losh			name = function()
118fd2b19b3SKyle Evans				return OnOff(color.highlight("A") ..
119aedd6be5SKyle Evans				    "CPI       :", core.acpi)
120088b4f5fSWarner Losh			end,
121*a51f9f0cSKyle Evans			func = core.setACPI,
122088b4f5fSWarner Losh			alias = {"a", "A"}
123088b4f5fSWarner Losh		},
124088b4f5fSWarner Losh		-- safe mode
125088b4f5fSWarner Losh		{
126a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
127088b4f5fSWarner Losh			name = function()
12857099121SKyle Evans				return OnOff("Safe " .. color.highlight("M") ..
129aedd6be5SKyle Evans				    "ode  :", core.sm)
130088b4f5fSWarner Losh			end,
131*a51f9f0cSKyle Evans			func = core.setSafeMode,
132088b4f5fSWarner Losh			alias = {"m", "M"}
133088b4f5fSWarner Losh		},
134088b4f5fSWarner Losh		-- single user
135088b4f5fSWarner Losh		{
136a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
137088b4f5fSWarner Losh			name = function()
138fd2b19b3SKyle Evans				return OnOff(color.highlight("S") ..
139aedd6be5SKyle Evans				    "ingle user:", core.su)
140088b4f5fSWarner Losh			end,
141*a51f9f0cSKyle Evans			func = core.setSingleUser,
142088b4f5fSWarner Losh			alias = {"s", "S"}
143088b4f5fSWarner Losh		},
144088b4f5fSWarner Losh		-- verbose boot
145088b4f5fSWarner Losh		{
146a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
147088b4f5fSWarner Losh			name = function()
148fd2b19b3SKyle Evans				return OnOff(color.highlight("V") ..
149aedd6be5SKyle Evans				    "erbose    :", core.verbose)
150088b4f5fSWarner Losh			end,
151*a51f9f0cSKyle Evans			func = core.setVerbose,
152088b4f5fSWarner Losh			alias = {"v", "V"}
153088b4f5fSWarner Losh		},
154d8757746SKyle Evans	},
155aedd6be5SKyle Evans}
156088b4f5fSWarner Losh
157088b4f5fSWarner Loshmenu.welcome = {
158303253e5SKyle Evans	entries = function()
159aedd6be5SKyle Evans		local menu_entries = menu.welcome.all_entries
160303253e5SKyle Evans		-- Swap the first two menu items on single user boot
1619f71d421SKyle Evans		if core.isSingleUserBoot() then
1629a0904b0SKyle Evans			-- We'll cache the swapped menu, for performance
1639f71d421SKyle Evans			if menu.welcome.swapped_menu ~= nil then
164aedd6be5SKyle Evans				return menu.welcome.swapped_menu
1659a0904b0SKyle Evans			end
1665c1b5165SKyle Evans			-- Shallow copy the table
167aedd6be5SKyle Evans			menu_entries = core.shallowCopyTable(menu_entries)
1685c1b5165SKyle Evans
1699a0904b0SKyle Evans			-- Swap the first two menu entries
1704b6da14cSKyle Evans			menu_entries[1], menu_entries[2] =
171aedd6be5SKyle Evans			    menu_entries[2], menu_entries[1]
172303253e5SKyle Evans
1739a0904b0SKyle Evans			-- Then set their names to their alternate names
1749a0904b0SKyle Evans			menu_entries[1].name, menu_entries[2].name =
1759a0904b0SKyle Evans			    menu_entries[1].alternate_name,
176aedd6be5SKyle Evans			    menu_entries[2].alternate_name
177aedd6be5SKyle Evans			menu.welcome.swapped_menu = menu_entries
178303253e5SKyle Evans		end
179aedd6be5SKyle Evans		return menu_entries
180303253e5SKyle Evans	end,
181303253e5SKyle Evans	all_entries = {
182088b4f5fSWarner Losh		-- boot multi user
183088b4f5fSWarner Losh		{
184a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
185*a51f9f0cSKyle Evans			name = color.highlight("B") .. "oot Multi user " ..
186*a51f9f0cSKyle Evans			    color.highlight("[Enter]"),
1875c1b5165SKyle Evans			-- Not a standard menu entry function!
188*a51f9f0cSKyle Evans			alternate_name = color.highlight("B") ..
189*a51f9f0cSKyle Evans			    "oot Multi user",
190088b4f5fSWarner Losh			func = function()
191aedd6be5SKyle Evans				core.setSingleUser(false)
192aedd6be5SKyle Evans				core.boot()
193088b4f5fSWarner Losh			end,
194b458bf0dSKyle Evans			alias = {"b", "B"}
195088b4f5fSWarner Losh		},
196088b4f5fSWarner Losh		-- boot single user
197088b4f5fSWarner Losh		{
198a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
199*a51f9f0cSKyle Evans			name = "Boot " .. color.highlight("S") .. "ingle user",
2005c1b5165SKyle Evans			-- Not a standard menu entry function!
201*a51f9f0cSKyle Evans			alternate_name = "Boot " .. color.highlight("S") ..
202*a51f9f0cSKyle Evans			    "ingle user " .. color.highlight("[Enter]"),
203088b4f5fSWarner Losh			func = function()
204aedd6be5SKyle Evans				core.setSingleUser(true)
205aedd6be5SKyle Evans				core.boot()
206088b4f5fSWarner Losh			end,
207088b4f5fSWarner Losh			alias = {"s", "S"}
208088b4f5fSWarner Losh		},
209088b4f5fSWarner Losh		-- escape to interpreter
210088b4f5fSWarner Losh		{
211a7cf0562SKyle Evans			entry_type = core.MENU_RETURN,
212*a51f9f0cSKyle Evans			name = color.highlight("Esc") .. "ape to loader prompt",
213ef625845SKyle Evans			func = function()
214aedd6be5SKyle Evans				loader.setenv("autoboot_delay", "NO")
215ef625845SKyle Evans			end,
21639006570SKyle Evans			alias = {core.KEYSTR_ESCAPE}
217088b4f5fSWarner Losh		},
218088b4f5fSWarner Losh		-- reboot
219088b4f5fSWarner Losh		{
220a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
221*a51f9f0cSKyle Evans			name = color.highlight("R") .. "eboot",
222088b4f5fSWarner Losh			func = function()
223aedd6be5SKyle Evans				loader.perform("reboot")
224088b4f5fSWarner Losh			end,
225088b4f5fSWarner Losh			alias = {"r", "R"}
226088b4f5fSWarner Losh		},
227088b4f5fSWarner Losh		{
228a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
229088b4f5fSWarner Losh		},
230088b4f5fSWarner Losh		{
231a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
232*a51f9f0cSKyle Evans			name = "Options:",
233088b4f5fSWarner Losh		},
234088b4f5fSWarner Losh		-- kernel options
235088b4f5fSWarner Losh		{
236a7cf0562SKyle Evans			entry_type = core.MENU_CAROUSEL_ENTRY,
237ada26c4aSKyle Evans			carousel_id = "kernel",
238ada26c4aSKyle Evans			items = core.kernelList,
239ada26c4aSKyle Evans			name = function(idx, choice, all_choices)
2409f71d421SKyle Evans				if #all_choices == 0 then
241aedd6be5SKyle Evans					return "Kernel: "
242088b4f5fSWarner Losh				end
243b1b1f2b8SKyle Evans
244aedd6be5SKyle Evans				local is_default = (idx == 1)
245aedd6be5SKyle Evans				local kernel_name = ""
246aedd6be5SKyle Evans				local name_color
2479f71d421SKyle Evans				if is_default then
248aedd6be5SKyle Evans					name_color = color.escapef(color.GREEN)
249aedd6be5SKyle Evans					kernel_name = "default/"
250bcf48a15SKyle Evans				else
251aedd6be5SKyle Evans					name_color = color.escapef(color.BLUE)
252b1b1f2b8SKyle Evans				end
253fd2b19b3SKyle Evans				kernel_name = kernel_name .. name_color ..
254aedd6be5SKyle Evans				    choice .. color.default()
255fd2b19b3SKyle Evans				return color.highlight("K") .. "ernel: " ..
256fd2b19b3SKyle Evans				    kernel_name .. " (" .. idx .. " of " ..
257aedd6be5SKyle Evans				    #all_choices .. ")"
258088b4f5fSWarner Losh			end,
2595d1e2f83SKyle Evans			func = function(idx, choice, all_choices)
260aedd6be5SKyle Evans				config.selectkernel(choice)
261088b4f5fSWarner Losh			end,
262088b4f5fSWarner Losh			alias = {"k", "K"}
263088b4f5fSWarner Losh		},
264088b4f5fSWarner Losh		-- boot options
265088b4f5fSWarner Losh		{
266a7cf0562SKyle Evans			entry_type = core.MENU_SUBMENU,
267*a51f9f0cSKyle Evans			name = "Boot " .. color.highlight("O") .. "ptions",
2689a28f948SKyle Evans			submenu = menu.boot_options,
269088b4f5fSWarner Losh			alias = {"o", "O"}
270d8757746SKyle Evans		},
271d8757746SKyle Evans	},
272aedd6be5SKyle Evans}
273088b4f5fSWarner Losh
27420a81676SKyle Evansmenu.default = menu.welcome
27520a81676SKyle Evans
276088b4f5fSWarner Loshfunction menu.run(m)
277088b4f5fSWarner Losh
2789f71d421SKyle Evans	if menu.skip() then
279aedd6be5SKyle Evans		core.autoboot()
280aedd6be5SKyle Evans		return false
281088b4f5fSWarner Losh	end
282088b4f5fSWarner Losh
2839f71d421SKyle Evans	if m == nil then
28420a81676SKyle Evans		m = menu.default
285088b4f5fSWarner Losh	end
286088b4f5fSWarner Losh
287088b4f5fSWarner Losh	-- redraw screen
288aedd6be5SKyle Evans	screen.clear()
289aedd6be5SKyle Evans	screen.defcursor()
290aedd6be5SKyle Evans	local alias_table = drawer.drawscreen(m)
291088b4f5fSWarner Losh
292aedd6be5SKyle Evans	menu.autoboot()
293088b4f5fSWarner Losh
294aedd6be5SKyle Evans	cont = true
2959f71d421SKyle Evans	while cont do
296aedd6be5SKyle Evans		local key = io.getchar()
297088b4f5fSWarner Losh
298b458bf0dSKyle Evans		-- Special key behaviors
2999f71d421SKyle Evans		if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and
30020a81676SKyle Evans		    m ~= menu.default then
301aedd6be5SKyle Evans			break
3029f71d421SKyle Evans		elseif key == core.KEY_ENTER then
303aedd6be5SKyle Evans			core.boot()
304b458bf0dSKyle Evans			-- Should not return
305abc4f7e7SKyle Evans		end
306abc4f7e7SKyle Evans
307abc4f7e7SKyle Evans		key = string.char(key)
308088b4f5fSWarner Losh		-- check to see if key is an alias
309aedd6be5SKyle Evans		local sel_entry = nil
310088b4f5fSWarner Losh		for k, v in pairs(alias_table) do
3119f71d421SKyle Evans			if key == k then
312aedd6be5SKyle Evans				sel_entry = v
313088b4f5fSWarner Losh			end
314088b4f5fSWarner Losh		end
315088b4f5fSWarner Losh
316088b4f5fSWarner Losh		-- if we have an alias do the assigned action:
3179f71d421SKyle Evans		if sel_entry ~= nil then
3188d415029SKyle Evans			-- Get menu handler
319aedd6be5SKyle Evans			local handler = menu.handlers[sel_entry.entry_type]
3209f71d421SKyle Evans			if handler ~= nil then
3218d415029SKyle Evans				-- The handler's return value indicates whether
3228d415029SKyle Evans				-- we need to exit this menu. An omitted return
3238d415029SKyle Evans				-- value means "continue" by default.
324aedd6be5SKyle Evans				cont = handler(m, sel_entry)
3259f71d421SKyle Evans				if cont == nil then
326aedd6be5SKyle Evans					cont = true
327aefcaa7eSKyle Evans				end
328088b4f5fSWarner Losh			end
329088b4f5fSWarner Losh			-- if we got an alias key the screen is out of date:
330aedd6be5SKyle Evans			screen.clear()
331aedd6be5SKyle Evans			screen.defcursor()
332aedd6be5SKyle Evans			alias_table = drawer.drawscreen(m)
333088b4f5fSWarner Losh		end
334088b4f5fSWarner Losh	end
335088b4f5fSWarner Losh
33620a81676SKyle Evans	if m == menu.default then
337aedd6be5SKyle Evans		screen.defcursor()
338aedd6be5SKyle Evans		print("Exiting menu!")
339aedd6be5SKyle Evans		return false
340088b4f5fSWarner Losh	end
341088b4f5fSWarner Losh
342aedd6be5SKyle Evans	return true
343088b4f5fSWarner Loshend
344088b4f5fSWarner Losh
345088b4f5fSWarner Loshfunction menu.skip()
3469f71d421SKyle Evans	if core.isSerialBoot() then
347aedd6be5SKyle Evans		return true
348088b4f5fSWarner Losh	end
349aedd6be5SKyle Evans	local c = string.lower(loader.getenv("console") or "")
3509f71d421SKyle Evans	if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then
351aedd6be5SKyle Evans		return true
352088b4f5fSWarner Losh	end
353088b4f5fSWarner Losh
354aedd6be5SKyle Evans	c = string.lower(loader.getenv("beastie_disable") or "")
355aedd6be5SKyle Evans	print("beastie_disable", c)
356aedd6be5SKyle Evans	return c == "yes"
357088b4f5fSWarner Loshend
358088b4f5fSWarner Losh
359088b4f5fSWarner Loshfunction menu.autoboot()
36049550489SKyle Evans	if menu.already_autoboot then
361aedd6be5SKyle Evans		return
362088b4f5fSWarner Losh	end
363aedd6be5SKyle Evans	menu.already_autoboot = true
364088b4f5fSWarner Losh
365aedd6be5SKyle Evans	local ab = loader.getenv("autoboot_delay")
3669f71d421SKyle Evans	if ab ~= nil and ab:lower() == "no" then
367aedd6be5SKyle Evans		return
3689f71d421SKyle Evans	elseif tonumber(ab) == -1 then
369aedd6be5SKyle Evans		core.boot()
370088b4f5fSWarner Losh	end
371aedd6be5SKyle Evans	ab = tonumber(ab) or 10
372088b4f5fSWarner Losh
373aedd6be5SKyle Evans	local x = loader.getenv("loader_menu_timeout_x") or 5
374aedd6be5SKyle Evans	local y = loader.getenv("loader_menu_timeout_y") or 22
375088b4f5fSWarner Losh
376aedd6be5SKyle Evans	local endtime = loader.time() + ab
377aedd6be5SKyle Evans	local time
378088b4f5fSWarner Losh
379088b4f5fSWarner Losh	repeat
380aedd6be5SKyle Evans		time = endtime - loader.time()
381aedd6be5SKyle Evans		screen.setcursor(x, y)
38257099121SKyle Evans		print("Autoboot in " .. time ..
38357099121SKyle Evans		    " seconds, hit [Enter] to boot" ..
384aedd6be5SKyle Evans		    " or any other key to stop     ")
385aedd6be5SKyle Evans		screen.defcursor()
3869f71d421SKyle Evans		if io.ischar() then
387aedd6be5SKyle Evans			local ch = io.getchar()
3889f71d421SKyle Evans			if ch == core.KEY_ENTER then
389aedd6be5SKyle Evans				break
390088b4f5fSWarner Losh			else
391088b4f5fSWarner Losh				-- erase autoboot msg
392aedd6be5SKyle Evans				screen.setcursor(0, y)
393088b4f5fSWarner Losh				print("                                        "
394aedd6be5SKyle Evans				    .. "                                        ")
395aedd6be5SKyle Evans				screen.defcursor()
396aedd6be5SKyle Evans				return
397088b4f5fSWarner Losh			end
398088b4f5fSWarner Losh		end
399088b4f5fSWarner Losh
400aedd6be5SKyle Evans		loader.delay(50000)
401aedd6be5SKyle Evans	until time <= 0
402aedd6be5SKyle Evans	core.boot()
403088b4f5fSWarner Losh
404088b4f5fSWarner Loshend
405088b4f5fSWarner Losh
406aedd6be5SKyle Evansreturn menu
407