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