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