]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/normal/menu.c
a83aeccafceb976043c29ac688c514f8d3f27a6b
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / normal / menu.c
1 /* menu.c - General supporting functionality for menus. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/normal.h>
21 #include <grub/misc.h>
22 #include <grub/loader.h>
23 #include <grub/mm.h>
24 #include <grub/time.h>
25 #include <grub/env.h>
26 #include <grub/menu_viewer.h>
27 #include <grub/command.h>
28 #include <grub/parser.h>
29 #include <grub/auth.h>
30 #include <grub/i18n.h>
31 #include <grub/term.h>
32 #include <grub/script_sh.h>
33 #include <grub/gfxterm.h>
34 #include <grub/dl.h>
35 #include <grub/env.h>
36
37 int g_ventoy_menu_refresh = 0;
38 int g_ventoy_memdisk_mode = 0;
39 int g_ventoy_iso_raw = 0;
40 int g_ventoy_iso_uefi_drv = 0;
41 int g_ventoy_last_entry = -1;
42 int g_ventoy_suppress_esc = 0;
43 int g_ventoy_menu_esc = 0;
44 int g_ventoy_fn_mutex = 0;
45 int g_ventoy_terminal_output = 0;
46
47 /* Time to delay after displaying an error message about a default/fallback
48 entry failing to boot. */
49 #define DEFAULT_ENTRY_ERROR_DELAY_MS 2500
50
51 grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
52 int nested) = NULL;
53
54 enum timeout_style {
55 TIMEOUT_STYLE_MENU,
56 TIMEOUT_STYLE_COUNTDOWN,
57 TIMEOUT_STYLE_HIDDEN
58 };
59
60 struct timeout_style_name {
61 const char *name;
62 enum timeout_style style;
63 } timeout_style_names[] = {
64 {"menu", TIMEOUT_STYLE_MENU},
65 {"countdown", TIMEOUT_STYLE_COUNTDOWN},
66 {"hidden", TIMEOUT_STYLE_HIDDEN},
67 {NULL, 0}
68 };
69
70 /* Wait until the user pushes any key so that the user
71 can see what happened. */
72 void
73 grub_wait_after_message (void)
74 {
75 grub_uint64_t endtime;
76 grub_xputs ("\n");
77 grub_printf_ (N_("Press any key to continue..."));
78 grub_refresh ();
79
80 endtime = grub_get_time_ms () + 10000;
81
82 while (grub_get_time_ms () < endtime
83 && grub_getkey_noblock () == GRUB_TERM_NO_KEY);
84
85 grub_xputs ("\n");
86 }
87
88 /* Get a menu entry by its index in the entry list. */
89 grub_menu_entry_t
90 grub_menu_get_entry (grub_menu_t menu, int no)
91 {
92 grub_menu_entry_t e;
93
94 for (e = menu->entry_list; e && no > 0; e = e->next, no--)
95 ;
96
97 return e;
98 }
99
100 /* Get the index of a menu entry associated with a given hotkey, or -1. */
101 static int
102 get_entry_index_by_hotkey (grub_menu_t menu, int hotkey)
103 {
104 grub_menu_entry_t entry;
105 int i;
106
107 for (i = 0, entry = menu->entry_list; i < menu->size;
108 i++, entry = entry->next)
109 if (entry->hotkey == hotkey)
110 return i;
111
112 return -1;
113 }
114
115 /* Return the timeout style. If the variable "timeout_style" is not set or
116 invalid, default to TIMEOUT_STYLE_MENU. */
117 static enum timeout_style
118 get_timeout_style (void)
119 {
120 const char *val;
121 struct timeout_style_name *style_name;
122
123 val = grub_env_get ("timeout_style");
124 if (!val)
125 return TIMEOUT_STYLE_MENU;
126
127 for (style_name = timeout_style_names; style_name->name; style_name++)
128 if (grub_strcmp (style_name->name, val) == 0)
129 return style_name->style;
130
131 return TIMEOUT_STYLE_MENU;
132 }
133
134 /* Return the current timeout. If the variable "timeout" is not set or
135 invalid, return -1. */
136 int
137 grub_menu_get_timeout (void)
138 {
139 const char *val;
140 int timeout;
141
142 val = grub_env_get ("timeout");
143 if (! val)
144 return -1;
145
146 grub_error_push ();
147
148 timeout = (int) grub_strtoul (val, 0, 0);
149
150 /* If the value is invalid, unset the variable. */
151 if (grub_errno != GRUB_ERR_NONE)
152 {
153 grub_env_unset ("timeout");
154 grub_errno = GRUB_ERR_NONE;
155 timeout = -1;
156 }
157
158 grub_error_pop ();
159
160 return timeout;
161 }
162
163 /* Set current timeout in the variable "timeout". */
164 void
165 grub_menu_set_timeout (int timeout)
166 {
167 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
168 if (timeout > 0)
169 {
170 char buf[16];
171
172 grub_snprintf (buf, sizeof (buf), "%d", timeout);
173 grub_env_set ("timeout", buf);
174 }
175 }
176
177 /* Get the first entry number from the value of the environment variable NAME,
178 which is a space-separated list of non-negative integers. The entry number
179 which is returned is stripped from the value of NAME. If no entry number
180 can be found, -1 is returned. */
181 static int
182 get_and_remove_first_entry_number (const char *name)
183 {
184 const char *val;
185 char *tail;
186 int entry;
187
188 val = grub_env_get (name);
189 if (! val)
190 return -1;
191
192 grub_error_push ();
193
194 entry = (int) grub_strtoul (val, &tail, 0);
195
196 if (grub_errno == GRUB_ERR_NONE)
197 {
198 /* Skip whitespace to find the next digit. */
199 while (*tail && grub_isspace (*tail))
200 tail++;
201 grub_env_set (name, tail);
202 }
203 else
204 {
205 grub_env_unset (name);
206 grub_errno = GRUB_ERR_NONE;
207 entry = -1;
208 }
209
210 grub_error_pop ();
211
212 return entry;
213 }
214
215 /* Run a menu entry. */
216 static void
217 grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
218 {
219 grub_err_t err = GRUB_ERR_NONE;
220 int errs_before;
221 grub_menu_t menu = NULL;
222 char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
223 const char *ptr, *chosen, *def;
224 grub_size_t sz = 0;
225
226 if (entry->restricted)
227 err = grub_auth_check_authentication (entry->users);
228
229 if (err)
230 {
231 grub_print_error ();
232 grub_errno = GRUB_ERR_NONE;
233 return;
234 }
235
236 errs_before = grub_err_printed_errors;
237
238 chosen = grub_env_get ("chosen");
239 def = grub_env_get ("default");
240
241 if (entry->submenu)
242 {
243 grub_env_context_open ();
244 menu = grub_zalloc (sizeof (*menu));
245 if (! menu)
246 return;
247 grub_env_set_menu (menu);
248 if (auto_boot)
249 grub_env_set ("timeout", "0");
250 }
251
252 for (ptr = entry->id; *ptr; ptr++)
253 sz += (*ptr == '>') ? 2 : 1;
254 if (chosen)
255 {
256 oldchosen = grub_strdup (chosen);
257 if (!oldchosen)
258 grub_print_error ();
259 }
260 if (def)
261 {
262 olddefault = grub_strdup (def);
263 if (!olddefault)
264 grub_print_error ();
265 }
266 sz++;
267 if (chosen)
268 sz += grub_strlen (chosen);
269 sz++;
270 buf = grub_malloc (sz);
271 if (!buf)
272 grub_print_error ();
273 else
274 {
275 optr = buf;
276 if (chosen)
277 {
278 optr = grub_stpcpy (optr, chosen);
279 *optr++ = '>';
280 }
281 for (ptr = entry->id; *ptr; ptr++)
282 {
283 if (*ptr == '>')
284 *optr++ = '>';
285 *optr++ = *ptr;
286 }
287 *optr = 0;
288 grub_env_set ("chosen", buf);
289 grub_env_export ("chosen");
290 grub_free (buf);
291 }
292
293 for (ptr = def; ptr && *ptr; ptr++)
294 {
295 if (ptr[0] == '>' && ptr[1] == '>')
296 {
297 ptr++;
298 continue;
299 }
300 if (ptr[0] == '>')
301 break;
302 }
303
304 if (ptr && ptr[0] && ptr[1])
305 grub_env_set ("default", ptr + 1);
306 else
307 grub_env_unset ("default");
308
309 grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
310
311 if (errs_before != grub_err_printed_errors)
312 grub_wait_after_message ();
313
314 errs_before = grub_err_printed_errors;
315
316 if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
317 /* Implicit execution of boot, only if something is loaded. */
318 grub_command_execute ("boot", 0, 0);
319
320 if (errs_before != grub_err_printed_errors)
321 grub_wait_after_message ();
322
323 if (entry->submenu)
324 {
325 if (menu && menu->size)
326 {
327 grub_show_menu (menu, 1, auto_boot);
328 grub_normal_free_menu (menu);
329 }
330 grub_env_context_close ();
331 }
332 if (oldchosen)
333 grub_env_set ("chosen", oldchosen);
334 else
335 grub_env_unset ("chosen");
336 if (olddefault)
337 grub_env_set ("default", olddefault);
338 else
339 grub_env_unset ("default");
340 grub_env_unset ("timeout");
341 }
342
343 /* Execute ENTRY from the menu MENU, falling back to entries specified
344 in the environment variable "fallback" if it fails. CALLBACK is a
345 pointer to a struct of function pointers which are used to allow the
346 caller provide feedback to the user. */
347 static void
348 grub_menu_execute_with_fallback (grub_menu_t menu,
349 grub_menu_entry_t entry,
350 int autobooted,
351 grub_menu_execute_callback_t callback,
352 void *callback_data)
353 {
354 int fallback_entry;
355
356 callback->notify_booting (entry, callback_data);
357
358 grub_menu_execute_entry (entry, 1);
359
360 /* Deal with fallback entries. */
361 while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
362 >= 0)
363 {
364 grub_print_error ();
365 grub_errno = GRUB_ERR_NONE;
366
367 entry = grub_menu_get_entry (menu, fallback_entry);
368 callback->notify_fallback (entry, callback_data);
369 grub_menu_execute_entry (entry, 1);
370 /* If the function call to execute the entry returns at all, then this is
371 taken to indicate a boot failure. For menu entries that do something
372 other than actually boot an operating system, this could assume
373 incorrectly that something failed. */
374 }
375
376 if (!autobooted)
377 callback->notify_failure (callback_data);
378 }
379
380 static struct grub_menu_viewer *viewers;
381
382 static void
383 menu_set_chosen_entry (int entry)
384 {
385 struct grub_menu_viewer *cur;
386 for (cur = viewers; cur; cur = cur->next)
387 cur->set_chosen_entry (entry, cur->data);
388 }
389
390 static void
391 menu_print_timeout (int timeout)
392 {
393 struct grub_menu_viewer *cur;
394 for (cur = viewers; cur; cur = cur->next)
395 cur->print_timeout (timeout, cur->data);
396 }
397
398 static void
399 menu_fini (void)
400 {
401 struct grub_menu_viewer *cur, *next;
402 for (cur = viewers; cur; cur = next)
403 {
404 next = cur->next;
405 cur->fini (cur->data);
406 grub_free (cur);
407 }
408 viewers = NULL;
409 }
410
411 static void
412 menu_init (int entry, grub_menu_t menu, int nested)
413 {
414 struct grub_term_output *term;
415 int gfxmenu = 0;
416
417 FOR_ACTIVE_TERM_OUTPUTS(term)
418 if (term->fullscreen)
419 {
420 if (grub_env_get ("theme"))
421 {
422 if (!grub_gfxmenu_try_hook)
423 {
424 grub_dl_load ("gfxmenu");
425 grub_print_error ();
426 }
427 if (grub_gfxmenu_try_hook)
428 {
429 grub_err_t err;
430 err = grub_gfxmenu_try_hook (entry, menu, nested);
431 if(!err)
432 {
433 gfxmenu = 1;
434 break;
435 }
436 }
437 else
438 grub_error (GRUB_ERR_BAD_MODULE,
439 N_("module `%s' isn't loaded"),
440 "gfxmenu");
441 grub_print_error ();
442 grub_wait_after_message ();
443 }
444 grub_errno = GRUB_ERR_NONE;
445 term->fullscreen ();
446 break;
447 }
448
449 FOR_ACTIVE_TERM_OUTPUTS(term)
450 {
451 grub_err_t err;
452
453 if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
454 continue;
455
456 err = grub_menu_try_text (term, entry, menu, nested);
457 if(!err)
458 continue;
459 grub_print_error ();
460 grub_errno = GRUB_ERR_NONE;
461 }
462 }
463
464 static void
465 clear_timeout (void)
466 {
467 struct grub_menu_viewer *cur;
468 for (cur = viewers; cur; cur = cur->next)
469 cur->clear_timeout (cur->data);
470 }
471
472 void
473 grub_menu_register_viewer (struct grub_menu_viewer *viewer)
474 {
475 viewer->next = viewers;
476 viewers = viewer;
477 }
478
479 static int
480 menuentry_eq (const char *id, const char *spec)
481 {
482 const char *ptr1, *ptr2;
483 ptr1 = id;
484 ptr2 = spec;
485 while (1)
486 {
487 if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
488 return 1;
489 if (*ptr2 == '>' && ptr2[1] != '>')
490 return 0;
491 if (*ptr2 == '>')
492 ptr2++;
493 if (*ptr1 != *ptr2)
494 return 0;
495 if (*ptr1 == 0)
496 return 1;
497 ptr1++;
498 ptr2++;
499 }
500 }
501
502
503 /* Get the entry number from the variable NAME. */
504 static int
505 get_entry_number (grub_menu_t menu, const char *name)
506 {
507 const char *val;
508 int entry;
509
510 val = grub_env_get (name);
511 if (! val)
512 return -1;
513
514 grub_error_push ();
515
516 entry = (int) grub_strtoul (val, 0, 0);
517
518 if (grub_errno == GRUB_ERR_BAD_NUMBER)
519 {
520 /* See if the variable matches the title of a menu entry. */
521 grub_menu_entry_t e = menu->entry_list;
522 int i;
523
524 grub_errno = GRUB_ERR_NONE;
525
526 for (i = 0; e; i++)
527 {
528 if (menuentry_eq (e->title, val)
529 || menuentry_eq (e->id, val))
530 {
531 entry = i;
532 break;
533 }
534 e = e->next;
535 }
536
537 if (! e)
538 entry = -1;
539 }
540
541 if (grub_errno != GRUB_ERR_NONE)
542 {
543 grub_errno = GRUB_ERR_NONE;
544 entry = -1;
545 }
546
547 grub_error_pop ();
548
549 return entry;
550 }
551
552 /* Check whether a second has elapsed since the last tick. If so, adjust
553 the timer and return 1; otherwise, return 0. */
554 static int
555 has_second_elapsed (grub_uint64_t *saved_time)
556 {
557 grub_uint64_t current_time;
558
559 current_time = grub_get_time_ms ();
560 if (current_time - *saved_time >= 1000)
561 {
562 *saved_time = current_time;
563 return 1;
564 }
565 else
566 return 0;
567 }
568
569 static void
570 print_countdown (struct grub_term_coordinate *pos, int n)
571 {
572 grub_term_restore_pos (pos);
573 /* NOTE: Do not remove the trailing space characters.
574 They are required to clear the line. */
575 grub_printf ("%d ", n);
576 grub_refresh ();
577 }
578
579 #define GRUB_MENU_PAGE_SIZE 10
580
581 /* Show the menu and handle menu entry selection. Returns the menu entry
582 index that should be executed or -1 if no entry should be executed (e.g.,
583 Esc pressed to exit a sub-menu or switching menu viewers).
584 If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
585 entry to be executed is a result of an automatic default selection because
586 of the timeout. */
587 static int
588 run_menu (grub_menu_t menu, int nested, int *auto_boot)
589 {
590 const char *cmdstr;
591 grub_uint64_t saved_time;
592 int default_entry,current_entry;
593 int timeout;
594 enum timeout_style timeout_style;
595
596 default_entry = get_entry_number (menu, "default");
597
598 if (g_ventoy_suppress_esc)
599 default_entry = 1;
600 else if (g_ventoy_last_entry >= 0 && g_ventoy_last_entry < menu->size) {
601 default_entry = g_ventoy_last_entry;
602 }
603 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
604 the first entry. */
605 else if (default_entry < 0 || default_entry >= menu->size)
606 default_entry = 0;
607
608 timeout = grub_menu_get_timeout ();
609 if (timeout < 0)
610 /* If there is no timeout, the "countdown" and "hidden" styles result in
611 the system doing nothing and providing no or very little indication
612 why. Technically this is what the user asked for, but it's not very
613 useful and likely to be a source of confusion, so we disallow this. */
614 grub_env_unset ("timeout_style");
615
616 timeout_style = get_timeout_style ();
617
618 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN
619 || timeout_style == TIMEOUT_STYLE_HIDDEN)
620 {
621 static struct grub_term_coordinate *pos;
622 int entry = -1;
623
624 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
625 {
626 pos = grub_term_save_pos ();
627 print_countdown (pos, timeout);
628 }
629
630 /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
631 or the timeout expires. */
632 saved_time = grub_get_time_ms ();
633 while (1)
634 {
635 int key;
636
637 key = grub_getkey_noblock ();
638 if (key != GRUB_TERM_NO_KEY)
639 {
640 entry = get_entry_index_by_hotkey (menu, key);
641 if (entry >= 0)
642 break;
643 }
644 if (key == GRUB_TERM_ESC)
645 {
646 timeout = -1;
647 break;
648 }
649
650 if (timeout > 0 && has_second_elapsed (&saved_time))
651 {
652 timeout--;
653 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
654 print_countdown (pos, timeout);
655 }
656
657 if (timeout == 0)
658 /* We will fall through to auto-booting the default entry. */
659 break;
660 }
661
662 grub_env_unset ("timeout");
663 grub_env_unset ("timeout_style");
664 if (entry >= 0)
665 {
666 *auto_boot = 0;
667 return entry;
668 }
669 }
670
671 /* If timeout is 0, drawing is pointless (and ugly). */
672 if (timeout == 0)
673 {
674 *auto_boot = 1;
675 return default_entry;
676 }
677
678 current_entry = default_entry;
679
680 refresh:
681 menu_init (current_entry, menu, nested);
682
683 /* Initialize the time. */
684 saved_time = grub_get_time_ms ();
685
686 timeout = grub_menu_get_timeout ();
687
688 if (timeout > 0)
689 menu_print_timeout (timeout);
690 else
691 clear_timeout ();
692
693 while (1)
694 {
695 int c;
696 timeout = grub_menu_get_timeout ();
697
698 if (grub_normal_exit_level)
699 return -1;
700
701 if (timeout > 0 && has_second_elapsed (&saved_time))
702 {
703 timeout--;
704 grub_menu_set_timeout (timeout);
705 menu_print_timeout (timeout);
706 }
707
708 if (timeout == 0)
709 {
710 grub_env_unset ("timeout");
711 *auto_boot = 1;
712 menu_fini ();
713 return default_entry;
714 }
715
716 c = grub_getkey_noblock ();
717
718 /* Negative values are returned on error. */
719 if ((c != GRUB_TERM_NO_KEY) && (c > 0))
720 {
721 if (timeout >= 0)
722 {
723 grub_env_unset ("timeout");
724 grub_env_unset ("fallback");
725 clear_timeout ();
726 }
727
728 switch (c)
729 {
730 case GRUB_TERM_KEY_HOME:
731 case GRUB_TERM_CTRL | 'a':
732 current_entry = 0;
733 menu_set_chosen_entry (current_entry);
734 break;
735
736 case GRUB_TERM_KEY_END:
737 case GRUB_TERM_CTRL | 'e':
738 current_entry = menu->size - 1;
739 menu_set_chosen_entry (current_entry);
740 break;
741
742 case GRUB_TERM_KEY_UP:
743 case GRUB_TERM_CTRL | 'p':
744 case '^':
745 if (current_entry > 0)
746 current_entry--;
747 menu_set_chosen_entry (current_entry);
748 break;
749
750 case GRUB_TERM_CTRL | 'n':
751 case GRUB_TERM_KEY_DOWN:
752 case 'v':
753 if (current_entry < menu->size - 1)
754 current_entry++;
755 menu_set_chosen_entry (current_entry);
756 break;
757
758 case GRUB_TERM_CTRL | 'g':
759 case GRUB_TERM_KEY_PPAGE:
760 if (current_entry < GRUB_MENU_PAGE_SIZE)
761 current_entry = 0;
762 else
763 current_entry -= GRUB_MENU_PAGE_SIZE;
764 menu_set_chosen_entry (current_entry);
765 break;
766
767 case GRUB_TERM_CTRL | 'c':
768 case GRUB_TERM_KEY_NPAGE:
769 if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
770 current_entry += GRUB_MENU_PAGE_SIZE;
771 else
772 current_entry = menu->size - 1;
773 menu_set_chosen_entry (current_entry);
774 break;
775
776 case '\n':
777 case '\r':
778 // case GRUB_TERM_KEY_RIGHT:
779 case GRUB_TERM_CTRL | 'f':
780 menu_fini ();
781 *auto_boot = 0;
782 return current_entry;
783
784 case GRUB_TERM_ESC:
785 if (nested && 0 == g_ventoy_suppress_esc)
786 {
787 menu_fini ();
788 return -1;
789 }
790 break;
791
792 case 'c':
793 menu_fini ();
794 grub_cmdline_run (1, 0);
795 goto refresh;
796
797 case 'e':
798 menu_fini ();
799 {
800 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
801 if (e)
802 grub_menu_entry_run (e);
803 }
804 goto refresh;
805
806 case GRUB_TERM_KEY_F2:
807 case '2':
808 if (0 == g_ventoy_fn_mutex) {
809 cmdstr = grub_env_get("VTOY_F2_CMD");
810 if (cmdstr)
811 {
812 menu_fini ();
813 g_ventoy_fn_mutex = 1;
814 grub_script_execute_sourcecode(cmdstr);
815 g_ventoy_fn_mutex = 0;
816 goto refresh;
817 }
818 }
819 break;
820 case GRUB_TERM_KEY_F3:
821 case '3':
822 if (0 == g_ventoy_fn_mutex) {
823 cmdstr = grub_env_get("VTOY_F3_CMD");
824 if (cmdstr)
825 {
826 menu_fini ();
827 grub_script_execute_sourcecode(cmdstr);
828 goto refresh;
829 }
830 }
831 break;
832 case GRUB_TERM_KEY_F4:
833 case '4':
834 if (0 == g_ventoy_fn_mutex) {
835 cmdstr = grub_env_get("VTOY_F4_CMD");
836 if (cmdstr)
837 {
838 menu_fini ();
839 g_ventoy_fn_mutex = 1;
840 grub_script_execute_sourcecode(cmdstr);
841 g_ventoy_fn_mutex = 0;
842 goto refresh;
843 }
844 }
845 break;
846 case GRUB_TERM_KEY_F5:
847 case '5':
848 if (0 == g_ventoy_fn_mutex) {
849 cmdstr = grub_env_get("VTOY_F5_CMD");
850 if (cmdstr)
851 {
852 menu_fini ();
853 g_ventoy_fn_mutex = 1;
854 grub_script_execute_sourcecode(cmdstr);
855 g_ventoy_fn_mutex = 0;
856 goto refresh;
857 }
858 }
859 break;
860 case GRUB_TERM_KEY_F6:
861 case '6':
862 if (0 == g_ventoy_fn_mutex) {
863 cmdstr = grub_env_get("VTOY_F6_CMD");
864 if (cmdstr)
865 {
866 menu_fini ();
867 g_ventoy_fn_mutex = 1;
868 grub_script_execute_sourcecode(cmdstr);
869 g_ventoy_fn_mutex = 0;
870 goto refresh;
871 }
872 }
873 break;
874 case GRUB_TERM_KEY_F7:
875 menu_fini ();
876 if (g_ventoy_terminal_output == 0)
877 {
878 grub_script_execute_sourcecode("terminal_output console");
879 g_ventoy_terminal_output = 1;
880 }
881 else
882 {
883 grub_script_execute_sourcecode("terminal_output gfxterm");
884 g_ventoy_terminal_output = 0;
885 }
886 goto refresh;
887 case GRUB_TERM_KEY_F1:
888 case '1':
889 menu_fini ();
890 g_ventoy_memdisk_mode = 1 - g_ventoy_memdisk_mode;
891 g_ventoy_menu_refresh = 1;
892 goto refresh;
893
894 case (GRUB_TERM_CTRL | 'i'):
895 menu_fini ();
896 g_ventoy_iso_raw = 1 - g_ventoy_iso_raw;
897 g_ventoy_menu_refresh = 1;
898 goto refresh;
899
900 case (GRUB_TERM_CTRL | 'u'):
901 menu_fini ();
902 g_ventoy_iso_uefi_drv = 1 - g_ventoy_iso_uefi_drv;
903 g_ventoy_menu_refresh = 1;
904 goto refresh;
905
906 default:
907 {
908 int entry;
909
910 entry = get_entry_index_by_hotkey (menu, c);
911 if (entry >= 0)
912 {
913 menu_fini ();
914 *auto_boot = 0;
915 return entry;
916 }
917 }
918 break;
919 }
920 }
921 }
922
923 /* Never reach here. */
924 }
925
926 /* Callback invoked immediately before a menu entry is executed. */
927 static void
928 notify_booting (grub_menu_entry_t entry,
929 void *userdata __attribute__((unused)))
930 {
931 grub_printf (" ");
932 grub_printf_ (N_("Booting `%s'"), entry->title);
933 grub_printf ("\n\n");
934 }
935
936 /* Callback invoked when a default menu entry executed because of a timeout
937 has failed and an attempt will be made to execute the next fallback
938 entry, ENTRY. */
939 static void
940 notify_fallback (grub_menu_entry_t entry,
941 void *userdata __attribute__((unused)))
942 {
943 grub_printf ("\n ");
944 grub_printf_ (N_("Falling back to `%s'"), entry->title);
945 grub_printf ("\n\n");
946 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
947 }
948
949 /* Callback invoked when a menu entry has failed and there is no remaining
950 fallback entry to attempt. */
951 static void
952 notify_execution_failure (void *userdata __attribute__((unused)))
953 {
954 if (grub_errno != GRUB_ERR_NONE)
955 {
956 grub_print_error ();
957 grub_errno = GRUB_ERR_NONE;
958 }
959 grub_printf ("\n ");
960 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
961 grub_wait_after_message ();
962 }
963
964 /* Callbacks used by the text menu to provide user feedback when menu entries
965 are executed. */
966 static struct grub_menu_execute_callback execution_callback =
967 {
968 .notify_booting = notify_booting,
969 .notify_fallback = notify_fallback,
970 .notify_failure = notify_execution_failure
971 };
972
973 static grub_err_t
974 show_menu (grub_menu_t menu, int nested, int autobooted)
975 {
976 const char *def;
977 def = grub_env_get("VTOY_DEFAULT_IMAGE");
978
979 while (1)
980 {
981 int boot_entry;
982 grub_menu_entry_t e;
983 int auto_boot;
984
985 boot_entry = run_menu (menu, nested, &auto_boot);
986 if (boot_entry < 0)
987 break;
988
989 if (auto_boot && def && grub_strcmp(def, "VTOY_EXIT") == 0) {
990 grub_exit();
991 }
992
993 if (autobooted == 0 && auto_boot == 0) {
994 g_ventoy_last_entry = boot_entry;
995 if (g_ventoy_menu_esc)
996 break;
997 }
998
999 e = grub_menu_get_entry (menu, boot_entry);
1000 if (! e)
1001 continue; /* Menu is empty. */
1002
1003 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RET", 8) == 0)
1004 break;
1005
1006 grub_cls ();
1007
1008 if (auto_boot)
1009 grub_menu_execute_with_fallback (menu, e, autobooted,
1010 &execution_callback, 0);
1011 else
1012 grub_menu_execute_entry (e, 0);
1013 if (autobooted)
1014 break;
1015
1016 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RUN_RET", 12) == 0)
1017 break;
1018 }
1019
1020 return GRUB_ERR_NONE;
1021 }
1022
1023 grub_err_t
1024 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
1025 {
1026 grub_err_t err1, err2;
1027
1028 while (1)
1029 {
1030 err1 = show_menu (menu, nested, autoboot);
1031 autoboot = 0;
1032 grub_print_error ();
1033
1034 if (grub_normal_exit_level)
1035 break;
1036
1037 err2 = grub_auth_check_authentication (NULL);
1038 if (err2)
1039 {
1040 grub_print_error ();
1041 grub_errno = GRUB_ERR_NONE;
1042 continue;
1043 }
1044
1045 break;
1046 }
1047
1048 return err1;
1049 }