xref: /freebsd/stand/lua/menu.lua (revision ed107537b43cabf7a18e73a17856a9d9e170c6e9)
1088b4f5fSWarner Losh--
272e39d71SKyle Evans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
372e39d71SKyle Evans--
4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
5e12ff891SKyle Evans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6088b4f5fSWarner Losh-- All rights reserved.
7088b4f5fSWarner Losh--
8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
10088b4f5fSWarner Losh-- are met:
11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
12088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
14088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
15088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
16088b4f5fSWarner Losh--
17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27088b4f5fSWarner Losh-- SUCH DAMAGE.
28088b4f5fSWarner Losh--
29088b4f5fSWarner Losh-- $FreeBSD$
30088b4f5fSWarner Losh--
31088b4f5fSWarner Losh
3276c75816SKyle Evanslocal cli = require("cli")
33aedd6be5SKyle Evanslocal core = require("core")
34aedd6be5SKyle Evanslocal color = require("color")
35aedd6be5SKyle Evanslocal config = require("config")
36aedd6be5SKyle Evanslocal screen = require("screen")
37aedd6be5SKyle Evanslocal drawer = require("drawer")
38088b4f5fSWarner Losh
39aedd6be5SKyle Evanslocal menu = {}
40c8518398SKyle Evans
41beafe961SKyle Evanslocal drawn_menu
4243f7d9d1SKyle Evanslocal return_menu_entry = {
4343f7d9d1SKyle Evans	entry_type = core.MENU_RETURN,
4443f7d9d1SKyle Evans	name = "Back to main menu" .. color.highlight(" [Backspace]"),
4543f7d9d1SKyle Evans}
46ca16d83fSKyle Evans
4704af4229SKyle Evanslocal function OnOff(str, value)
4804af4229SKyle Evans	if value then
498ce1744fSKyle Evans		return str .. color.escapefg(color.GREEN) .. "On" ..
506dd078dfSToomas Soome		    color.resetfg()
51e15abd1fSKyle Evans	else
528ce1744fSKyle Evans		return str .. color.escapefg(color.RED) .. "off" ..
536dd078dfSToomas Soome		    color.resetfg()
54e15abd1fSKyle Evans	end
55e15abd1fSKyle Evansend
56e15abd1fSKyle Evans
579ed6f9efSKyle Evanslocal function bootenvSet(env)
587efc058fSKyle Evans	loader.setenv("vfs.root.mountfrom", env)
597efc058fSKyle Evans	loader.setenv("currdev", env .. ":")
607efc058fSKyle Evans	config.reload()
617efc058fSKyle Evansend
627efc058fSKyle Evans
63b5746545SKyle Evans-- Module exports
648d415029SKyle Evansmenu.handlers = {
658d415029SKyle Evans	-- Menu handlers take the current menu and selected entry as parameters,
668d415029SKyle Evans	-- and should return a boolean indicating whether execution should
678d415029SKyle Evans	-- continue or not. The return value may be omitted if this entry should
688d415029SKyle Evans	-- have no bearing on whether we continue or not, indicating that we
698d415029SKyle Evans	-- should just continue after execution.
70e2df27e3SKyle Evans	[core.MENU_ENTRY] = function(_, entry)
718d415029SKyle Evans		-- run function
72aedd6be5SKyle Evans		entry.func()
738d415029SKyle Evans	end,
74e2df27e3SKyle Evans	[core.MENU_CAROUSEL_ENTRY] = function(_, entry)
758d415029SKyle Evans		-- carousel (rotating) functionality
76aedd6be5SKyle Evans		local carid = entry.carousel_id
77aedd6be5SKyle Evans		local caridx = config.getCarouselIndex(carid)
784f437f9eSKyle Evans		local choices = entry.items
794f437f9eSKyle Evans		if type(choices) == "function" then
804f437f9eSKyle Evans			choices = choices()
814f437f9eSKyle Evans		end
829f71d421SKyle Evans		if #choices > 0 then
83aedd6be5SKyle Evans			caridx = (caridx % #choices) + 1
84aedd6be5SKyle Evans			config.setCarouselIndex(carid, caridx)
85aedd6be5SKyle Evans			entry.func(caridx, choices[caridx], choices)
868d415029SKyle Evans		end
878d415029SKyle Evans	end,
88e2df27e3SKyle Evans	[core.MENU_SUBMENU] = function(_, entry)
89da9ab827SKyle Evans		menu.process(entry.submenu)
908d415029SKyle Evans	end,
91e2df27e3SKyle Evans	[core.MENU_RETURN] = function(_, entry)
928d415029SKyle Evans		-- allow entry to have a function/side effect
939f71d421SKyle Evans		if entry.func ~= nil then
94aedd6be5SKyle Evans			entry.func()
958d415029SKyle Evans		end
96aedd6be5SKyle Evans		return false
978d415029SKyle Evans	end,
98aedd6be5SKyle Evans}
99280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome
100088b4f5fSWarner Losh
1017efc058fSKyle Evansmenu.boot_environments = {
1027efc058fSKyle Evans	entries = {
1037efc058fSKyle Evans		-- return to welcome menu
10443f7d9d1SKyle Evans		return_menu_entry,
1057efc058fSKyle Evans		{
1067efc058fSKyle Evans			entry_type = core.MENU_CAROUSEL_ENTRY,
1077efc058fSKyle Evans			carousel_id = "be_active",
1087efc058fSKyle Evans			items = core.bootenvList,
1097efc058fSKyle Evans			name = function(idx, choice, all_choices)
1107efc058fSKyle Evans				if #all_choices == 0 then
1117efc058fSKyle Evans					return "Active: "
1127efc058fSKyle Evans				end
1137efc058fSKyle Evans
1147efc058fSKyle Evans				local is_default = (idx == 1)
1157efc058fSKyle Evans				local bootenv_name = ""
1167efc058fSKyle Evans				local name_color
1177efc058fSKyle Evans				if is_default then
1188ce1744fSKyle Evans					name_color = color.escapefg(color.GREEN)
1197efc058fSKyle Evans				else
1208ce1744fSKyle Evans					name_color = color.escapefg(color.BLUE)
1217efc058fSKyle Evans				end
1227efc058fSKyle Evans				bootenv_name = bootenv_name .. name_color ..
1238ce1744fSKyle Evans				    choice .. color.resetfg()
1247efc058fSKyle Evans				return color.highlight("A").."ctive: " ..
1257efc058fSKyle Evans				    bootenv_name .. " (" .. idx .. " of " ..
1267efc058fSKyle Evans				    #all_choices .. ")"
1277efc058fSKyle Evans			end,
128e2df27e3SKyle Evans			func = function(_, choice, _)
1297efc058fSKyle Evans				bootenvSet(choice)
1307efc058fSKyle Evans			end,
1317efc058fSKyle Evans			alias = {"a", "A"},
1327efc058fSKyle Evans		},
1337efc058fSKyle Evans		{
1347efc058fSKyle Evans			entry_type = core.MENU_ENTRY,
135277f38abSMariusz Zaborski			visible = function()
136277f38abSMariusz Zaborski				return core.isRewinded() == false
137277f38abSMariusz Zaborski			end,
1387efc058fSKyle Evans			name = function()
1397efc058fSKyle Evans				return color.highlight("b") .. "ootfs: " ..
1407efc058fSKyle Evans				    core.bootenvDefault()
1417efc058fSKyle Evans			end,
1427efc058fSKyle Evans			func = function()
1437efc058fSKyle Evans				-- Reset active boot environment to the default
1447efc058fSKyle Evans				config.setCarouselIndex("be_active", 1)
1457efc058fSKyle Evans				bootenvSet(core.bootenvDefault())
1467efc058fSKyle Evans			end,
1477efc058fSKyle Evans			alias = {"b", "B"},
1487efc058fSKyle Evans		},
1497efc058fSKyle Evans	},
1507efc058fSKyle Evans}
1517efc058fSKyle Evans
152088b4f5fSWarner Loshmenu.boot_options = {
153d8757746SKyle Evans	entries = {
154088b4f5fSWarner Losh		-- return to welcome menu
15543f7d9d1SKyle Evans		return_menu_entry,
156088b4f5fSWarner Losh		-- load defaults
157088b4f5fSWarner Losh		{
158a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
159a51f9f0cSKyle Evans			name = "Load System " .. color.highlight("D") ..
160a51f9f0cSKyle Evans			    "efaults",
161a51f9f0cSKyle Evans			func = core.setDefaults,
1623cd5547bSKyle Evans			alias = {"d", "D"},
163088b4f5fSWarner Losh		},
164088b4f5fSWarner Losh		{
165a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
166088b4f5fSWarner Losh		},
167088b4f5fSWarner Losh		{
168a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
169a51f9f0cSKyle Evans			name = "Boot Options:",
170088b4f5fSWarner Losh		},
171088b4f5fSWarner Losh		-- acpi
172088b4f5fSWarner Losh		{
173a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
174c1ab36f5SKyle Evans			visible = core.isSystem386,
175088b4f5fSWarner Losh			name = function()
176fd2b19b3SKyle Evans				return OnOff(color.highlight("A") ..
177aedd6be5SKyle Evans				    "CPI       :", core.acpi)
178088b4f5fSWarner Losh			end,
179a51f9f0cSKyle Evans			func = core.setACPI,
1803cd5547bSKyle Evans			alias = {"a", "A"},
181088b4f5fSWarner Losh		},
182088b4f5fSWarner Losh		-- safe mode
183088b4f5fSWarner Losh		{
184a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
185088b4f5fSWarner Losh			name = function()
18657099121SKyle Evans				return OnOff("Safe " .. color.highlight("M") ..
187aedd6be5SKyle Evans				    "ode  :", core.sm)
188088b4f5fSWarner Losh			end,
189a51f9f0cSKyle Evans			func = core.setSafeMode,
1903cd5547bSKyle Evans			alias = {"m", "M"},
191088b4f5fSWarner Losh		},
192088b4f5fSWarner Losh		-- single user
193088b4f5fSWarner Losh		{
194a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
195088b4f5fSWarner Losh			name = function()
196fd2b19b3SKyle Evans				return OnOff(color.highlight("S") ..
197aedd6be5SKyle Evans				    "ingle user:", core.su)
198088b4f5fSWarner Losh			end,
199a51f9f0cSKyle Evans			func = core.setSingleUser,
2003cd5547bSKyle Evans			alias = {"s", "S"},
201088b4f5fSWarner Losh		},
202088b4f5fSWarner Losh		-- verbose boot
203088b4f5fSWarner Losh		{
204a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
205088b4f5fSWarner Losh			name = function()
206fd2b19b3SKyle Evans				return OnOff(color.highlight("V") ..
207aedd6be5SKyle Evans				    "erbose    :", core.verbose)
208088b4f5fSWarner Losh			end,
209a51f9f0cSKyle Evans			func = core.setVerbose,
2103cd5547bSKyle Evans			alias = {"v", "V"},
211088b4f5fSWarner Losh		},
212d8757746SKyle Evans	},
213aedd6be5SKyle Evans}
214088b4f5fSWarner Losh
215088b4f5fSWarner Loshmenu.welcome = {
216303253e5SKyle Evans	entries = function()
217aedd6be5SKyle Evans		local menu_entries = menu.welcome.all_entries
218d2187b39SRyan Moeller		local multi_user = menu_entries.multi_user
219d2187b39SRyan Moeller		local single_user = menu_entries.single_user
220d2187b39SRyan Moeller		local boot_entry_1, boot_entry_2
2219f71d421SKyle Evans		if core.isSingleUserBoot() then
222d2187b39SRyan Moeller			-- Swap the first two menu items on single user boot.
223d2187b39SRyan Moeller			-- We'll cache the alternate entries for performance.
224d2187b39SRyan Moeller			local alts = menu_entries.alts
225d2187b39SRyan Moeller			if alts == nil then
226d2187b39SRyan Moeller				single_user = core.deepCopyTable(single_user)
227d2187b39SRyan Moeller				multi_user = core.deepCopyTable(multi_user)
228d2187b39SRyan Moeller				single_user.name = single_user.alternate_name
229d2187b39SRyan Moeller				multi_user.name = multi_user.alternate_name
230d2187b39SRyan Moeller				menu_entries.alts = {
231d2187b39SRyan Moeller					single_user = single_user,
232d2187b39SRyan Moeller					multi_user = multi_user,
233d2187b39SRyan Moeller				}
234d2187b39SRyan Moeller			else
235d2187b39SRyan Moeller				single_user = alts.single_user
236d2187b39SRyan Moeller				multi_user = alts.multi_user
2379a0904b0SKyle Evans			end
238d2187b39SRyan Moeller			boot_entry_1, boot_entry_2 = single_user, multi_user
239d2187b39SRyan Moeller		else
240d2187b39SRyan Moeller			boot_entry_1, boot_entry_2 = multi_user, single_user
241303253e5SKyle Evans		end
242d2187b39SRyan Moeller		return {
243d2187b39SRyan Moeller			boot_entry_1,
244d2187b39SRyan Moeller			boot_entry_2,
245d2187b39SRyan Moeller			menu_entries.prompt,
246d2187b39SRyan Moeller			menu_entries.reboot,
2478f3b3610SWarner Losh			menu_entries.console,
248d2187b39SRyan Moeller			{
249d2187b39SRyan Moeller				entry_type = core.MENU_SEPARATOR,
250d2187b39SRyan Moeller			},
251d2187b39SRyan Moeller			{
252d2187b39SRyan Moeller				entry_type = core.MENU_SEPARATOR,
253d2187b39SRyan Moeller				name = "Options:",
254d2187b39SRyan Moeller			},
255d2187b39SRyan Moeller			menu_entries.kernel_options,
256d2187b39SRyan Moeller			menu_entries.boot_options,
257277f38abSMariusz Zaborski			menu_entries.zpool_checkpoints,
258d2187b39SRyan Moeller			menu_entries.boot_envs,
259d2187b39SRyan Moeller			menu_entries.chainload,
260e7ccd5b4SWarner Losh			menu_entries.vendor,
261d2187b39SRyan Moeller		}
262303253e5SKyle Evans	end,
263303253e5SKyle Evans	all_entries = {
264d2187b39SRyan Moeller		multi_user = {
265a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
266a51f9f0cSKyle Evans			name = color.highlight("B") .. "oot Multi user " ..
267a51f9f0cSKyle Evans			    color.highlight("[Enter]"),
2685c1b5165SKyle Evans			-- Not a standard menu entry function!
269a51f9f0cSKyle Evans			alternate_name = color.highlight("B") ..
270a51f9f0cSKyle Evans			    "oot Multi user",
271088b4f5fSWarner Losh			func = function()
272aedd6be5SKyle Evans				core.setSingleUser(false)
273aedd6be5SKyle Evans				core.boot()
274088b4f5fSWarner Losh			end,
2753cd5547bSKyle Evans			alias = {"b", "B"},
276088b4f5fSWarner Losh		},
277d2187b39SRyan Moeller		single_user = {
278a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
279a51f9f0cSKyle Evans			name = "Boot " .. color.highlight("S") .. "ingle user",
2805c1b5165SKyle Evans			-- Not a standard menu entry function!
281a51f9f0cSKyle Evans			alternate_name = "Boot " .. color.highlight("S") ..
282a51f9f0cSKyle Evans			    "ingle user " .. color.highlight("[Enter]"),
283088b4f5fSWarner Losh			func = function()
284aedd6be5SKyle Evans				core.setSingleUser(true)
285aedd6be5SKyle Evans				core.boot()
286088b4f5fSWarner Losh			end,
2873cd5547bSKyle Evans			alias = {"s", "S"},
288088b4f5fSWarner Losh		},
2898f3b3610SWarner Losh		console = {
2908f3b3610SWarner Losh			entry_type = core.MENU_ENTRY,
2918f3b3610SWarner Losh			name = function()
2928f3b3610SWarner Losh				return color.highlight("C") .. "ons: " .. core.getConsoleName()
2938f3b3610SWarner Losh			end,
2948f3b3610SWarner Losh			func = function()
2958f3b3610SWarner Losh				core.nextConsoleChoice()
2968f3b3610SWarner Losh			end,
2978f3b3610SWarner Losh			alias = {"c", "C"},
2988f3b3610SWarner Losh		},
299d2187b39SRyan Moeller		prompt = {
300a7cf0562SKyle Evans			entry_type = core.MENU_RETURN,
301a51f9f0cSKyle Evans			name = color.highlight("Esc") .. "ape to loader prompt",
302ef625845SKyle Evans			func = function()
303aedd6be5SKyle Evans				loader.setenv("autoboot_delay", "NO")
304ef625845SKyle Evans			end,
3053cd5547bSKyle Evans			alias = {core.KEYSTR_ESCAPE},
306088b4f5fSWarner Losh		},
307d2187b39SRyan Moeller		reboot = {
308a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
309a51f9f0cSKyle Evans			name = color.highlight("R") .. "eboot",
310088b4f5fSWarner Losh			func = function()
311aedd6be5SKyle Evans				loader.perform("reboot")
312088b4f5fSWarner Losh			end,
3133cd5547bSKyle Evans			alias = {"r", "R"},
314088b4f5fSWarner Losh		},
315d2187b39SRyan Moeller		kernel_options = {
316a7cf0562SKyle Evans			entry_type = core.MENU_CAROUSEL_ENTRY,
317ada26c4aSKyle Evans			carousel_id = "kernel",
318ada26c4aSKyle Evans			items = core.kernelList,
319ada26c4aSKyle Evans			name = function(idx, choice, all_choices)
3209f71d421SKyle Evans				if #all_choices == 0 then
321aedd6be5SKyle Evans					return "Kernel: "
322088b4f5fSWarner Losh				end
323b1b1f2b8SKyle Evans
324aedd6be5SKyle Evans				local is_default = (idx == 1)
325aedd6be5SKyle Evans				local kernel_name = ""
326aedd6be5SKyle Evans				local name_color
3279f71d421SKyle Evans				if is_default then
3288ce1744fSKyle Evans					name_color = color.escapefg(color.GREEN)
329aedd6be5SKyle Evans					kernel_name = "default/"
330bcf48a15SKyle Evans				else
3318ce1744fSKyle Evans					name_color = color.escapefg(color.BLUE)
332b1b1f2b8SKyle Evans				end
333fd2b19b3SKyle Evans				kernel_name = kernel_name .. name_color ..
3348ce1744fSKyle Evans				    choice .. color.resetfg()
335fd2b19b3SKyle Evans				return color.highlight("K") .. "ernel: " ..
336fd2b19b3SKyle Evans				    kernel_name .. " (" .. idx .. " of " ..
337aedd6be5SKyle Evans				    #all_choices .. ")"
338088b4f5fSWarner Losh			end,
339e2df27e3SKyle Evans			func = function(_, choice, _)
340e414851fSKyle Evans				if loader.getenv("kernelname") ~= nil then
341e414851fSKyle Evans					loader.perform("unload")
342e414851fSKyle Evans				end
343322a2dddSKyle Evans				config.selectKernel(choice)
344088b4f5fSWarner Losh			end,
3453cd5547bSKyle Evans			alias = {"k", "K"},
346088b4f5fSWarner Losh		},
347d2187b39SRyan Moeller		boot_options = {
348a7cf0562SKyle Evans			entry_type = core.MENU_SUBMENU,
349a51f9f0cSKyle Evans			name = "Boot " .. color.highlight("O") .. "ptions",
3509a28f948SKyle Evans			submenu = menu.boot_options,
3513cd5547bSKyle Evans			alias = {"o", "O"},
352d8757746SKyle Evans		},
353277f38abSMariusz Zaborski		zpool_checkpoints = {
354277f38abSMariusz Zaborski			entry_type = core.MENU_ENTRY,
355277f38abSMariusz Zaborski			name = function()
35694510c29SKyle Evans				local rewind = "No"
357277f38abSMariusz Zaborski				if core.isRewinded() then
358277f38abSMariusz Zaborski					rewind = "Yes"
359277f38abSMariusz Zaborski				end
360277f38abSMariusz Zaborski				return "Rewind ZFS " .. color.highlight("C") ..
361277f38abSMariusz Zaborski					"heckpoint: " .. rewind
362277f38abSMariusz Zaborski			end,
363277f38abSMariusz Zaborski			func = function()
364277f38abSMariusz Zaborski				core.changeRewindCheckpoint()
365277f38abSMariusz Zaborski				if core.isRewinded() then
366277f38abSMariusz Zaborski					bootenvSet(
367277f38abSMariusz Zaborski					    core.bootenvDefaultRewinded())
368277f38abSMariusz Zaborski				else
369277f38abSMariusz Zaborski					bootenvSet(core.bootenvDefault())
370277f38abSMariusz Zaborski				end
371277f38abSMariusz Zaborski				config.setCarouselIndex("be_active", 1)
372277f38abSMariusz Zaborski			end,
373277f38abSMariusz Zaborski			visible = function()
374277f38abSMariusz Zaborski				return core.isZFSBoot() and
375277f38abSMariusz Zaborski				    core.isCheckpointed()
376277f38abSMariusz Zaborski			end,
377277f38abSMariusz Zaborski			alias = {"c", "C"},
378277f38abSMariusz Zaborski		},
379d2187b39SRyan Moeller		boot_envs = {
3807efc058fSKyle Evans			entry_type = core.MENU_SUBMENU,
3817efc058fSKyle Evans			visible = function()
3827efc058fSKyle Evans				return core.isZFSBoot() and
3837efc058fSKyle Evans				    #core.bootenvList() > 1
3847efc058fSKyle Evans			end,
3857efc058fSKyle Evans			name = "Boot " .. color.highlight("E") .. "nvironments",
3867efc058fSKyle Evans			submenu = menu.boot_environments,
3877efc058fSKyle Evans			alias = {"e", "E"},
3887efc058fSKyle Evans		},
389d2187b39SRyan Moeller		chainload = {
390bdf12807SKyle Evans			entry_type = core.MENU_ENTRY,
391bdf12807SKyle Evans			name = function()
392bdf12807SKyle Evans				return 'Chain' .. color.highlight("L") ..
393bdf12807SKyle Evans				    "oad " .. loader.getenv('chain_disk')
394bdf12807SKyle Evans			end,
395bdf12807SKyle Evans			func = function()
396bdf12807SKyle Evans				loader.perform("chain " ..
397bdf12807SKyle Evans				    loader.getenv('chain_disk'))
398bdf12807SKyle Evans			end,
399bdf12807SKyle Evans			visible = function()
400bdf12807SKyle Evans				return loader.getenv('chain_disk') ~= nil
401bdf12807SKyle Evans			end,
402bdf12807SKyle Evans			alias = {"l", "L"},
403bdf12807SKyle Evans		},
404e7ccd5b4SWarner Losh		vendor = {
405e7ccd5b4SWarner Losh			entry_type = core.MENU_ENTRY,
406556e66b7SWarner Losh			visible = function()
407556e66b7SWarner Losh				return false
408556e66b7SWarner Losh			end
409e7ccd5b4SWarner Losh		},
410d8757746SKyle Evans	},
411aedd6be5SKyle Evans}
412088b4f5fSWarner Losh
41320a81676SKyle Evansmenu.default = menu.welcome
41428384160SKyle Evans-- current_alias_table will be used to keep our alias table consistent across
41528384160SKyle Evans-- screen redraws, instead of relying on whatever triggered the redraw to update
41628384160SKyle Evans-- the local alias_table in menu.process.
41728384160SKyle Evansmenu.current_alias_table = {}
41820a81676SKyle Evans
4192a11b810SKyle Evansfunction menu.draw(menudef)
420beafe961SKyle Evans	-- Clear the screen, reset the cursor, then draw
421aedd6be5SKyle Evans	screen.clear()
4222a11b810SKyle Evans	menu.current_alias_table = drawer.drawscreen(menudef)
4232a11b810SKyle Evans	drawn_menu = menudef
424decacd91SKyle Evans	screen.defcursor()
42528384160SKyle Evansend
42628384160SKyle Evans
427ca16d83fSKyle Evans-- 'keypress' allows the caller to indicate that a key has been pressed that we
428ca16d83fSKyle Evans-- should process as our initial input.
4292a11b810SKyle Evansfunction menu.process(menudef, keypress)
4302a11b810SKyle Evans	assert(menudef ~= nil)
43128384160SKyle Evans
4322a11b810SKyle Evans	if drawn_menu ~= menudef then
4332a11b810SKyle Evans		menu.draw(menudef)
4347dcffa90SKyle Evans	end
435ca16d83fSKyle Evans
436da9ab827SKyle Evans	while true do
437ca16d83fSKyle Evans		local key = keypress or io.getchar()
438ca16d83fSKyle Evans		keypress = nil
439088b4f5fSWarner Losh
440b458bf0dSKyle Evans		-- Special key behaviors
4419f71d421SKyle Evans		if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and
4422a11b810SKyle Evans		    menudef ~= menu.default then
443aedd6be5SKyle Evans			break
4449f71d421SKyle Evans		elseif key == core.KEY_ENTER then
445aedd6be5SKyle Evans			core.boot()
446041929aaSKyle Evans			-- Should not return.  If it does, escape menu handling
447041929aaSKyle Evans			-- and drop to loader prompt.
448041929aaSKyle Evans			return false
449abc4f7e7SKyle Evans		end
450abc4f7e7SKyle Evans
451abc4f7e7SKyle Evans		key = string.char(key)
452088b4f5fSWarner Losh		-- check to see if key is an alias
453aedd6be5SKyle Evans		local sel_entry = nil
45428384160SKyle Evans		for k, v in pairs(menu.current_alias_table) do
4559f71d421SKyle Evans			if key == k then
456aedd6be5SKyle Evans				sel_entry = v
45728384160SKyle Evans				break
458088b4f5fSWarner Losh			end
459088b4f5fSWarner Losh		end
460088b4f5fSWarner Losh
461088b4f5fSWarner Losh		-- if we have an alias do the assigned action:
4629f71d421SKyle Evans		if sel_entry ~= nil then
463aedd6be5SKyle Evans			local handler = menu.handlers[sel_entry.entry_type]
4642a11b810SKyle Evans			assert(handler ~= nil)
465da9ab827SKyle Evans			-- The handler's return value indicates if we
466da9ab827SKyle Evans			-- need to exit this menu.  An omitted or true
467da9ab827SKyle Evans			-- return value means to continue.
4682a11b810SKyle Evans			if handler(menudef, sel_entry) == false then
469da9ab827SKyle Evans				return
470aefcaa7eSKyle Evans			end
47128384160SKyle Evans			-- If we got an alias key the screen is out of date...
47228384160SKyle Evans			-- redraw it.
4732a11b810SKyle Evans			menu.draw(menudef)
474088b4f5fSWarner Losh		end
475088b4f5fSWarner Losh	end
476088b4f5fSWarner Loshend
477088b4f5fSWarner Losh
478da9ab827SKyle Evansfunction menu.run()
479041929aaSKyle Evans	local autoboot_key
480c84dbc53SKyle Evans	local delay = loader.getenv("autoboot_delay")
481c84dbc53SKyle Evans
482c84dbc53SKyle Evans	if delay ~= nil and delay:lower() == "no" then
483c84dbc53SKyle Evans		delay = nil
484c84dbc53SKyle Evans	else
485c84dbc53SKyle Evans		delay = tonumber(delay) or 10
486c84dbc53SKyle Evans	end
487c84dbc53SKyle Evans
488c84dbc53SKyle Evans	if delay == -1 then
489c84dbc53SKyle Evans		core.boot()
490c84dbc53SKyle Evans		return
491c84dbc53SKyle Evans	end
492c84dbc53SKyle Evans
493beafe961SKyle Evans	menu.draw(menu.default)
494c84dbc53SKyle Evans
495041929aaSKyle Evans	if delay ~= nil then
496041929aaSKyle Evans		autoboot_key = menu.autoboot(delay)
497041929aaSKyle Evans
498041929aaSKyle Evans		-- autoboot_key should return the key pressed.  It will only
499041929aaSKyle Evans		-- return nil if we hit the timeout and executed the timeout
500041929aaSKyle Evans		-- command.  Bail out.
501041929aaSKyle Evans		if autoboot_key == nil then
502041929aaSKyle Evans			return
503041929aaSKyle Evans		end
504041929aaSKyle Evans	end
505ca16d83fSKyle Evans
506ca16d83fSKyle Evans	menu.process(menu.default, autoboot_key)
507beafe961SKyle Evans	drawn_menu = nil
508da9ab827SKyle Evans
509da9ab827SKyle Evans	screen.defcursor()
510da9ab827SKyle Evans	print("Exiting menu!")
511088b4f5fSWarner Loshend
512088b4f5fSWarner Losh
513c84dbc53SKyle Evansfunction menu.autoboot(delay)
5141495c98fSKyle Evans	local x = loader.getenv("loader_menu_timeout_x") or 4
5151495c98fSKyle Evans	local y = loader.getenv("loader_menu_timeout_y") or 23
516c84dbc53SKyle Evans	local endtime = loader.time() + delay
517aedd6be5SKyle Evans	local time
5183244729fSKyle Evans	local last
519088b4f5fSWarner Losh	repeat
520aedd6be5SKyle Evans		time = endtime - loader.time()
5213244729fSKyle Evans		if last == nil or last ~= time then
5223244729fSKyle Evans			last = time
523aedd6be5SKyle Evans			screen.setcursor(x, y)
52457099121SKyle Evans			print("Autoboot in " .. time ..
525*ed107537SKyle Evans			    " seconds. [Space] to pause")
526aedd6be5SKyle Evans			screen.defcursor()
5273244729fSKyle Evans		end
5289f71d421SKyle Evans		if io.ischar() then
529aedd6be5SKyle Evans			local ch = io.getchar()
5309f71d421SKyle Evans			if ch == core.KEY_ENTER then
531aedd6be5SKyle Evans				break
532088b4f5fSWarner Losh			else
533088b4f5fSWarner Losh				-- erase autoboot msg
534aedd6be5SKyle Evans				screen.setcursor(0, y)
53566964bbcSKyle Evans				print(string.rep(" ", 80))
536aedd6be5SKyle Evans				screen.defcursor()
53712b95c84SKyle Evans				return ch
538088b4f5fSWarner Losh			end
539088b4f5fSWarner Losh		end
540088b4f5fSWarner Losh
541aedd6be5SKyle Evans		loader.delay(50000)
542aedd6be5SKyle Evans	until time <= 0
543088b4f5fSWarner Losh
544a76f8a5bSKyle Evans	local cmd = loader.getenv("menu_timeout_command") or "boot"
545e9c3ceb1SKyle Evans	cli_execute_unparsed(cmd)
546041929aaSKyle Evans	return nil
547088b4f5fSWarner Loshend
548088b4f5fSWarner Losh
54976c75816SKyle Evans-- CLI commands
5508f7f3d08SKyle Evansfunction cli.menu()
55176c75816SKyle Evans	menu.run()
55276c75816SKyle Evansend
55376c75816SKyle Evans
554aedd6be5SKyle Evansreturn menu
555