]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/menu.lua
MFV r329715: 8997 ztest assertion failure in zil_lwb_write_issue
[FreeBSD/FreeBSD.git] / stand / lua / menu.lua
1 --
2 -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3 -- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
4 -- All rights reserved.
5 --
6 -- Redistribution and use in source and binary forms, with or without
7 -- modification, are permitted provided that the following conditions
8 -- are met:
9 -- 1. Redistributions of source code must retain the above copyright
10 --    notice, this list of conditions and the following disclaimer.
11 -- 2. Redistributions in binary form must reproduce the above copyright
12 --    notice, this list of conditions and the following disclaimer in the
13 --    documentation and/or other materials provided with the distribution.
14 --
15 -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 -- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 -- SUCH DAMAGE.
26 --
27 -- $FreeBSD$
28 --
29
30
31 local core = require("core")
32 local color = require("color")
33 local config = require("config")
34 local screen = require("screen")
35 local drawer = require("drawer")
36
37 local menu = {}
38
39 local skip
40 local run
41 local autoboot
42
43 local OnOff = function(str, b)
44         if b then
45                 return str .. color.escapef(color.GREEN) .. "On" ..
46                     color.escapef(color.WHITE)
47         else
48                 return str .. color.escapef(color.RED) .. "off" ..
49                     color.escapef(color.WHITE)
50         end
51 end
52
53 -- Module exports
54 menu.handlers = {
55         -- Menu handlers take the current menu and selected entry as parameters,
56         -- and should return a boolean indicating whether execution should
57         -- continue or not. The return value may be omitted if this entry should
58         -- have no bearing on whether we continue or not, indicating that we
59         -- should just continue after execution.
60         [core.MENU_ENTRY] = function(current_menu, entry)
61                 -- run function
62                 entry.func()
63         end,
64         [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry)
65                 -- carousel (rotating) functionality
66                 local carid = entry.carousel_id
67                 local caridx = config.getCarouselIndex(carid)
68                 local choices = entry.items
69                 if type(choices) == "function" then
70                         choices = choices()
71                 end
72                 if #choices > 0 then
73                         caridx = (caridx % #choices) + 1
74                         config.setCarouselIndex(carid, caridx)
75                         entry.func(caridx, choices[caridx], choices)
76                 end
77         end,
78         [core.MENU_SUBMENU] = function(current_menu, entry)
79                 -- recurse
80                 return menu.run(entry.submenu)
81         end,
82         [core.MENU_RETURN] = function(current_menu, entry)
83                 -- allow entry to have a function/side effect
84                 if entry.func ~= nil then
85                         entry.func()
86                 end
87                 return false
88         end,
89 }
90 -- loader menu tree is rooted at menu.welcome
91
92 menu.boot_options = {
93         entries = {
94                 -- return to welcome menu
95                 {
96                         entry_type = core.MENU_RETURN,
97                         name = "Back to main menu" ..
98                             color.highlight(" [Backspace]"),
99                 },
100                 -- load defaults
101                 {
102                         entry_type = core.MENU_ENTRY,
103                         name = "Load System " .. color.highlight("D") ..
104                             "efaults",
105                         func = core.setDefaults,
106                         alias = {"d", "D"}
107                 },
108                 {
109                         entry_type = core.MENU_SEPARATOR,
110                 },
111                 {
112                         entry_type = core.MENU_SEPARATOR,
113                         name = "Boot Options:",
114                 },
115                 -- acpi
116                 {
117                         entry_type = core.MENU_ENTRY,
118                         visible = core.isSystem386,
119                         name = function()
120                                 return OnOff(color.highlight("A") ..
121                                     "CPI       :", core.acpi)
122                         end,
123                         func = core.setACPI,
124                         alias = {"a", "A"}
125                 },
126                 -- safe mode
127                 {
128                         entry_type = core.MENU_ENTRY,
129                         name = function()
130                                 return OnOff("Safe " .. color.highlight("M") ..
131                                     "ode  :", core.sm)
132                         end,
133                         func = core.setSafeMode,
134                         alias = {"m", "M"}
135                 },
136                 -- single user
137                 {
138                         entry_type = core.MENU_ENTRY,
139                         name = function()
140                                 return OnOff(color.highlight("S") ..
141                                     "ingle user:", core.su)
142                         end,
143                         func = core.setSingleUser,
144                         alias = {"s", "S"}
145                 },
146                 -- verbose boot
147                 {
148                         entry_type = core.MENU_ENTRY,
149                         name = function()
150                                 return OnOff(color.highlight("V") ..
151                                     "erbose    :", core.verbose)
152                         end,
153                         func = core.setVerbose,
154                         alias = {"v", "V"}
155                 },
156         },
157 }
158
159 menu.welcome = {
160         entries = function()
161                 local menu_entries = menu.welcome.all_entries
162                 -- Swap the first two menu items on single user boot
163                 if core.isSingleUserBoot() then
164                         -- We'll cache the swapped menu, for performance
165                         if menu.welcome.swapped_menu ~= nil then
166                                 return menu.welcome.swapped_menu
167                         end
168                         -- Shallow copy the table
169                         menu_entries = core.shallowCopyTable(menu_entries)
170
171                         -- Swap the first two menu entries
172                         menu_entries[1], menu_entries[2] =
173                             menu_entries[2], menu_entries[1]
174
175                         -- Then set their names to their alternate names
176                         menu_entries[1].name, menu_entries[2].name =
177                             menu_entries[1].alternate_name,
178                             menu_entries[2].alternate_name
179                         menu.welcome.swapped_menu = menu_entries
180                 end
181                 return menu_entries
182         end,
183         all_entries = {
184                 -- boot multi user
185                 {
186                         entry_type = core.MENU_ENTRY,
187                         name = color.highlight("B") .. "oot Multi user " ..
188                             color.highlight("[Enter]"),
189                         -- Not a standard menu entry function!
190                         alternate_name = color.highlight("B") ..
191                             "oot Multi user",
192                         func = function()
193                                 core.setSingleUser(false)
194                                 core.boot()
195                         end,
196                         alias = {"b", "B"}
197                 },
198                 -- boot single user
199                 {
200                         entry_type = core.MENU_ENTRY,
201                         name = "Boot " .. color.highlight("S") .. "ingle user",
202                         -- Not a standard menu entry function!
203                         alternate_name = "Boot " .. color.highlight("S") ..
204                             "ingle user " .. color.highlight("[Enter]"),
205                         func = function()
206                                 core.setSingleUser(true)
207                                 core.boot()
208                         end,
209                         alias = {"s", "S"}
210                 },
211                 -- escape to interpreter
212                 {
213                         entry_type = core.MENU_RETURN,
214                         name = color.highlight("Esc") .. "ape to loader prompt",
215                         func = function()
216                                 loader.setenv("autoboot_delay", "NO")
217                         end,
218                         alias = {core.KEYSTR_ESCAPE}
219                 },
220                 -- reboot
221                 {
222                         entry_type = core.MENU_ENTRY,
223                         name = color.highlight("R") .. "eboot",
224                         func = function()
225                                 loader.perform("reboot")
226                         end,
227                         alias = {"r", "R"}
228                 },
229                 {
230                         entry_type = core.MENU_SEPARATOR,
231                 },
232                 {
233                         entry_type = core.MENU_SEPARATOR,
234                         name = "Options:",
235                 },
236                 -- kernel options
237                 {
238                         entry_type = core.MENU_CAROUSEL_ENTRY,
239                         carousel_id = "kernel",
240                         items = core.kernelList,
241                         name = function(idx, choice, all_choices)
242                                 if #all_choices == 0 then
243                                         return "Kernel: "
244                                 end
245
246                                 local is_default = (idx == 1)
247                                 local kernel_name = ""
248                                 local name_color
249                                 if is_default then
250                                         name_color = color.escapef(color.GREEN)
251                                         kernel_name = "default/"
252                                 else
253                                         name_color = color.escapef(color.BLUE)
254                                 end
255                                 kernel_name = kernel_name .. name_color ..
256                                     choice .. color.default()
257                                 return color.highlight("K") .. "ernel: " ..
258                                     kernel_name .. " (" .. idx .. " of " ..
259                                     #all_choices .. ")"
260                         end,
261                         func = function(idx, choice, all_choices)
262                                 config.selectkernel(choice)
263                         end,
264                         alias = {"k", "K"}
265                 },
266                 -- boot options
267                 {
268                         entry_type = core.MENU_SUBMENU,
269                         name = "Boot " .. color.highlight("O") .. "ptions",
270                         submenu = menu.boot_options,
271                         alias = {"o", "O"}
272                 },
273         },
274 }
275
276 menu.default = menu.welcome
277
278 function menu.run(m)
279
280         if menu.skip() then
281                 core.autoboot()
282                 return false
283         end
284
285         if m == nil then
286                 m = menu.default
287         end
288
289         -- redraw screen
290         screen.clear()
291         screen.defcursor()
292         local alias_table = drawer.drawscreen(m)
293
294         -- Might return nil, that's ok
295         local autoboot_key = menu.autoboot()
296
297         cont = true
298         while cont do
299                 local key = autoboot_key or io.getchar()
300                 autoboot_key = nil
301
302                 -- Special key behaviors
303                 if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and
304                     m ~= menu.default then
305                         break
306                 elseif key == core.KEY_ENTER then
307                         core.boot()
308                         -- Should not return
309                 end
310
311                 key = string.char(key)
312                 -- check to see if key is an alias
313                 local sel_entry = nil
314                 for k, v in pairs(alias_table) do
315                         if key == k then
316                                 sel_entry = v
317                         end
318                 end
319
320                 -- if we have an alias do the assigned action:
321                 if sel_entry ~= nil then
322                         -- Get menu handler
323                         local handler = menu.handlers[sel_entry.entry_type]
324                         if handler ~= nil then
325                                 -- The handler's return value indicates whether
326                                 -- we need to exit this menu. An omitted return
327                                 -- value means "continue" by default.
328                                 cont = handler(m, sel_entry)
329                                 if cont == nil then
330                                         cont = true
331                                 end
332                         end
333                         -- if we got an alias key the screen is out of date:
334                         screen.clear()
335                         screen.defcursor()
336                         alias_table = drawer.drawscreen(m)
337                 end
338         end
339
340         if m == menu.default then
341                 screen.defcursor()
342                 print("Exiting menu!")
343                 return false
344         end
345
346         return true
347 end
348
349 function menu.skip()
350         if core.isSerialBoot() then
351                 return true
352         end
353         local c = string.lower(loader.getenv("console") or "")
354         if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then
355                 return true
356         end
357
358         c = string.lower(loader.getenv("beastie_disable") or "")
359         print("beastie_disable", c)
360         return c == "yes"
361 end
362
363 function menu.autoboot()
364         if menu.already_autoboot then
365                 return nil
366         end
367         menu.already_autoboot = true
368
369         local ab = loader.getenv("autoboot_delay")
370         if ab ~= nil and ab:lower() == "no" then
371                 return nil
372         elseif tonumber(ab) == -1 then
373                 core.boot()
374         end
375         ab = tonumber(ab) or 10
376
377         local x = loader.getenv("loader_menu_timeout_x") or 5
378         local y = loader.getenv("loader_menu_timeout_y") or 22
379
380         local endtime = loader.time() + ab
381         local time
382
383         repeat
384                 time = endtime - loader.time()
385                 screen.setcursor(x, y)
386                 print("Autoboot in " .. time ..
387                     " seconds, hit [Enter] to boot" ..
388                     " or any other key to stop     ")
389                 screen.defcursor()
390                 if io.ischar() then
391                         local ch = io.getchar()
392                         if ch == core.KEY_ENTER then
393                                 break
394                         else
395                                 -- erase autoboot msg
396                                 screen.setcursor(0, y)
397                                 print("                                        "
398                                     .. "                                        ")
399                                 screen.defcursor()
400                                 return ch
401                         end
402                 end
403
404                 loader.delay(50000)
405         until time <= 0
406         core.boot()
407
408 end
409
410 return menu