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