]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/menu.lua
MFV r316873: 7233 dir_is_empty should open directory with CLOEXEC
[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
70                 if (#choices > 0) then
71                         caridx = (caridx % #choices) + 1;
72                         config.setCarouselIndex(carid, caridx);
73                         entry.func(caridx, choices[caridx], choices);
74                 end
75         end,
76         [core.MENU_SUBMENU] = function(current_menu, entry)
77                 -- recurse
78                 return menu.run(entry.submenu());
79         end,
80         [core.MENU_RETURN] = function(current_menu, entry)
81                 -- allow entry to have a function/side effect
82                 if (entry.func ~= nil) then
83                         entry.func();
84                 end
85                 return false;
86         end,
87 };
88 -- loader menu tree is rooted at menu.welcome
89
90 menu.boot_options = {
91         entries = {
92                 -- return to welcome menu
93                 {
94                         entry_type = core.MENU_RETURN,
95                         name = function()
96                                 return "Back to main menu" ..
97                                     color.highlight(" [Backspace]");
98                         end
99                 },
100
101                 -- load defaults
102                 {
103                         entry_type = core.MENU_ENTRY,
104                         name = function()
105                                 return "Load System " .. color.highlight("D") ..
106                                     "efaults";
107                         end,
108                         func = function()
109                                 core.setDefaults();
110                         end,
111                         alias = {"d", "D"}
112                 },
113
114                 {
115                         entry_type = core.MENU_SEPARATOR,
116                         name = function()
117                                 return "";
118                         end
119                 },
120
121                 {
122                         entry_type = core.MENU_SEPARATOR,
123                         name = function()
124                                 return "Boot Options:";
125                         end
126                 },
127
128                 -- acpi
129                 {
130                         entry_type = core.MENU_ENTRY,
131                         visible = core.isSystem386,
132                         name = function()
133                                 return OnOff(color.highlight("A") ..
134                                     "CPI       :", core.acpi);
135                         end,
136                         func = function()
137                                 core.setACPI();
138                         end,
139                         alias = {"a", "A"}
140                 },
141                 -- safe mode
142                 {
143                         entry_type = core.MENU_ENTRY,
144                         name = function()
145                                 return OnOff("Safe " .. color.highlight("M") ..
146                                     "ode  :", core.sm);
147                         end,
148                         func = function()
149                                 core.setSafeMode();
150                         end,
151                         alias = {"m", "M"}
152                 },
153                 -- single user
154                 {
155                         entry_type = core.MENU_ENTRY,
156                         name = function()
157                                 return OnOff(color.highlight("S") ..
158                                     "ingle user:", core.su);
159                         end,
160                         func = function()
161                                 core.setSingleUser();
162                         end,
163                         alias = {"s", "S"}
164                 },
165                 -- verbose boot
166                 {
167                         entry_type = core.MENU_ENTRY,
168                         name = function()
169                                 return OnOff(color.highlight("V") ..
170                                     "erbose    :", core.verbose);
171                         end,
172                         func = function()
173                                 core.setVerbose();
174                         end,
175                         alias = {"v", "V"}
176                 },
177         },
178 };
179
180 menu.welcome = {
181         entries = function()
182                 local menu_entries = menu.welcome.all_entries;
183                 -- Swap the first two menu items on single user boot
184                 if (core.isSingleUserBoot()) then
185                         -- We'll cache the swapped menu, for performance
186                         if (menu.welcome.swapped_menu ~= nil) then
187                                 return menu.welcome.swapped_menu;
188                         end
189                         -- Shallow copy the table
190                         menu_entries = core.shallowCopyTable(menu_entries);
191
192                         -- Swap the first two menu entries
193                         menu_entries[1], menu_entries[2] =
194                             menu_entries[2], menu_entries[1];
195
196                         -- Then set their names to their alternate names
197                         menu_entries[1].name, menu_entries[2].name =
198                             menu_entries[1].alternate_name,
199                             menu_entries[2].alternate_name;
200                         menu.welcome.swapped_menu = menu_entries;
201                 end
202                 return menu_entries;
203         end,
204         all_entries = {
205                 -- boot multi user
206                 {
207                         entry_type = core.MENU_ENTRY,
208                         name = function()
209                                 return color.highlight("B") ..
210                                     "oot Multi user " ..
211                                     color.highlight("[Enter]");
212                         end,
213                         -- Not a standard menu entry function!
214                         alternate_name = function()
215                                 return color.highlight("B") ..
216                                     "oot Multi user";
217                         end,
218                         func = function()
219                                 core.setSingleUser(false);
220                                 core.boot();
221                         end,
222                         alias = {"b", "B"}
223                 },
224
225                 -- boot single user
226                 {
227                         entry_type = core.MENU_ENTRY,
228                         name = function()
229                                 return "Boot " .. color.highlight("S") ..
230                                     "ingle user";
231                         end,
232                         -- Not a standard menu entry function!
233                         alternate_name = function()
234                                 return "Boot " .. color.highlight("S") ..
235                                     "ingle user " .. color.highlight("[Enter]");
236                         end,
237                         func = function()
238                                 core.setSingleUser(true);
239                                 core.boot();
240                         end,
241                         alias = {"s", "S"}
242                 },
243
244                 -- escape to interpreter
245                 {
246                         entry_type = core.MENU_RETURN,
247                         name = function()
248                                 return color.highlight("Esc") ..
249                                     "ape to loader prompt";
250                         end,
251                         func = function()
252                                 loader.setenv("autoboot_delay", "NO");
253                         end,
254                         alias = {core.KEYSTR_ESCAPE}
255                 },
256
257                 -- reboot
258                 {
259                         entry_type = core.MENU_ENTRY,
260                         name = function()
261                                 return color.highlight("R") .. "eboot";
262                         end,
263                         func = function()
264                                 loader.perform("reboot");
265                         end,
266                         alias = {"r", "R"}
267                 },
268
269
270                 {
271                         entry_type = core.MENU_SEPARATOR,
272                         name = function()
273                                 return "";
274                         end
275                 },
276
277                 {
278                         entry_type = core.MENU_SEPARATOR,
279                         name = function()
280                                 return "Options:";
281                         end
282                 },
283
284                 -- kernel options
285                 {
286                         entry_type = core.MENU_CAROUSEL_ENTRY,
287                         carousel_id = "kernel",
288                         items = core.kernelList,
289                         name = function(idx, choice, all_choices)
290                                 if (#all_choices == 0) then
291                                         return "Kernel: ";
292                                 end
293
294                                 local is_default = (idx == 1);
295                                 local kernel_name = "";
296                                 local name_color;
297                                 if (is_default) then
298                                         name_color = color.escapef(color.GREEN);
299                                         kernel_name = "default/";
300                                 else
301                                         name_color = color.escapef(color.BLUE);
302                                 end
303                                 kernel_name = kernel_name .. name_color ..
304                                     choice .. color.default();
305                                 return color.highlight("K") .. "ernel: " ..
306                                     kernel_name .. " (" .. idx .. " of " ..
307                                     #all_choices .. ")";
308                         end,
309                         func = function(idx, choice, all_choices)
310                                 config.selectkernel(choice);
311                         end,
312                         alias = {"k", "K"}
313                 },
314
315                 -- boot options
316                 {
317                         entry_type = core.MENU_SUBMENU,
318                         name = function()
319                                 return "Boot " .. color.highlight("O") ..
320                                     "ptions";
321                         end,
322                         submenu = function()
323                                 return menu.boot_options;
324                         end,
325                         alias = {"o", "O"}
326                 },
327         },
328 };
329
330 function menu.run(m)
331
332         if (menu.skip()) then
333                 core.autoboot();
334                 return false;
335         end
336
337         if (m == nil) then
338                 m = menu.welcome;
339         end
340
341         -- redraw screen
342         screen.clear();
343         screen.defcursor();
344         local alias_table = drawer.drawscreen(m);
345
346         menu.autoboot();
347
348         cont = true;
349         while (cont) do
350                 local key = io.getchar();
351
352                 -- Special key behaviors
353                 if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
354                     (m ~= menu.welcome) then
355                         break;
356                 elseif (key == core.KEY_ENTER) then
357                         core.boot();
358                         -- Should not return
359                 end
360
361                 key = string.char(key)
362                 -- check to see if key is an alias
363                 local sel_entry = nil;
364                 for k, v in pairs(alias_table) do
365                         if (key == k) then
366                                 sel_entry = v;
367                         end
368                 end
369
370                 -- if we have an alias do the assigned action:
371                 if (sel_entry ~= nil) then
372                         -- Get menu handler
373                         local handler = menu.handlers[sel_entry.entry_type];
374                         if (handler ~= nil) then
375                                 -- The handler's return value indicates whether
376                                 -- we need to exit this menu. An omitted return
377                                 -- value means "continue" by default.
378                                 cont = handler(m, sel_entry);
379                                 if (cont == nil) then
380                                         cont = true;
381                                 end
382                         end
383                         -- if we got an alias key the screen is out of date:
384                         screen.clear();
385                         screen.defcursor();
386                         alias_table = drawer.drawscreen(m);
387                 end
388         end
389
390         if (m == menu.welcome) then
391                 screen.defcursor();
392                 print("Exiting menu!");
393                 config.loadelf();
394                 return false;
395         end
396
397         return true;
398 end
399
400 function menu.skip()
401         if (core.isSerialBoot()) then
402                 return true;
403         end
404         local c = string.lower(loader.getenv("console") or "");
405         if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
406                 return true;
407         end
408
409         c = string.lower(loader.getenv("beastie_disable") or "");
410         print("beastie_disable", c);
411         return c == "yes";
412 end
413
414 function menu.autoboot()
415         if (menu.already_autoboot == true) then
416                 return;
417         end
418         menu.already_autoboot = true;
419
420         local ab = loader.getenv("autoboot_delay");
421         if (ab ~= nil) and (ab:lower() == "no") then
422                 return;
423         elseif (tonumber(ab) == -1) then
424                 core.boot();
425         end
426         ab = tonumber(ab) or 10;
427
428         local x = loader.getenv("loader_menu_timeout_x") or 5;
429         local y = loader.getenv("loader_menu_timeout_y") or 22;
430
431         local endtime = loader.time() + ab;
432         local time;
433
434         repeat
435                 time = endtime - loader.time();
436                 screen.setcursor(x, y);
437                 print("Autoboot in " .. time ..
438                     " seconds, hit [Enter] to boot" ..
439                     " or any other key to stop     ");
440                 screen.defcursor();
441                 if (io.ischar()) then
442                         local ch = io.getchar();
443                         if (ch == core.KEY_ENTER) then
444                                 break;
445                         else
446                                 -- erase autoboot msg
447                                 screen.setcursor(0, y);
448                                 print("                                        "
449                                     .. "                                        ");
450                                 screen.defcursor();
451                                 return;
452                         end
453                 end
454
455                 loader.delay(50000);
456         until time <= 0;
457         core.boot();
458
459 end
460
461 return menu;