]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/normal/menu.c
955608a033ac396338f61c9c74d8e6bbf57643a9
[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 menu_set_chosen_tip(grub_menu_t menu, int entry)
393 {
394 img_info *img;
395 grub_menu_entry_t e = grub_menu_get_entry (menu, entry);
396
397 g_ventoy_tip_msg1 = g_ventoy_tip_msg2 = NULL;
398 if (e && e->id && grub_strncmp(e->id, "VID_", 4) == 0)
399 {
400 img = (img_info *)(void *)grub_strtoul(e->id + 4, NULL, 16);
401 if (img)
402 {
403 g_ventoy_tip_msg1 = img->tip1;
404 g_ventoy_tip_msg2 = img->tip2;
405 }
406 }
407 }
408
409 static void
410 menu_set_chosen_entry (grub_menu_t menu, int entry)
411 {
412 struct grub_menu_viewer *cur;
413
414 menu_set_chosen_tip(menu, entry);
415 for (cur = viewers; cur; cur = cur->next)
416 cur->set_chosen_entry (entry, cur->data);
417 }
418
419 static void
420 menu_print_timeout (int timeout)
421 {
422 struct grub_menu_viewer *cur;
423 for (cur = viewers; cur; cur = cur->next)
424 cur->print_timeout (timeout, cur->data);
425 }
426
427 static void
428 menu_fini (void)
429 {
430 struct grub_menu_viewer *cur, *next;
431 for (cur = viewers; cur; cur = next)
432 {
433 next = cur->next;
434 cur->fini (cur->data);
435 grub_free (cur);
436 }
437 viewers = NULL;
438 }
439
440 static void
441 menu_init (int entry, grub_menu_t menu, int nested)
442 {
443 struct grub_term_output *term;
444 int gfxmenu = 0;
445
446 FOR_ACTIVE_TERM_OUTPUTS(term)
447 if (term->fullscreen)
448 {
449 if (grub_env_get ("theme"))
450 {
451 if (!grub_gfxmenu_try_hook)
452 {
453 grub_dl_load ("gfxmenu");
454 grub_print_error ();
455 }
456 if (grub_gfxmenu_try_hook)
457 {
458 grub_err_t err;
459 err = grub_gfxmenu_try_hook (entry, menu, nested);
460 if(!err)
461 {
462 gfxmenu = 1;
463 break;
464 }
465 }
466 else
467 grub_error (GRUB_ERR_BAD_MODULE,
468 N_("module `%s' isn't loaded"),
469 "gfxmenu");
470 grub_print_error ();
471 grub_wait_after_message ();
472 }
473 grub_errno = GRUB_ERR_NONE;
474 term->fullscreen ();
475 break;
476 }
477
478 FOR_ACTIVE_TERM_OUTPUTS(term)
479 {
480 grub_err_t err;
481
482 if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
483 continue;
484
485 err = grub_menu_try_text (term, entry, menu, nested);
486 if(!err)
487 continue;
488 grub_print_error ();
489 grub_errno = GRUB_ERR_NONE;
490 }
491 }
492
493 static void
494 clear_timeout (void)
495 {
496 struct grub_menu_viewer *cur;
497 for (cur = viewers; cur; cur = cur->next)
498 cur->clear_timeout (cur->data);
499 }
500
501 void
502 grub_menu_register_viewer (struct grub_menu_viewer *viewer)
503 {
504 viewer->next = viewers;
505 viewers = viewer;
506 }
507
508 static int
509 menuentry_eq (const char *id, const char *spec)
510 {
511 const char *ptr1, *ptr2;
512 ptr1 = id;
513 ptr2 = spec;
514 while (1)
515 {
516 if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
517 return 1;
518 if (*ptr2 == '>' && ptr2[1] != '>')
519 return 0;
520 if (*ptr2 == '>')
521 ptr2++;
522 if (*ptr1 != *ptr2)
523 return 0;
524 if (*ptr1 == 0)
525 return 1;
526 ptr1++;
527 ptr2++;
528 }
529 }
530
531
532 /* Get the entry number from the variable NAME. */
533 static int
534 get_entry_number (grub_menu_t menu, const char *name)
535 {
536 const char *val;
537 int entry;
538
539 val = grub_env_get (name);
540 if (! val)
541 return -1;
542
543 grub_error_push ();
544
545 entry = (int) grub_strtoul (val, 0, 0);
546
547 if (grub_errno == GRUB_ERR_BAD_NUMBER)
548 {
549 /* See if the variable matches the title of a menu entry. */
550 grub_menu_entry_t e = menu->entry_list;
551 int i;
552
553 grub_errno = GRUB_ERR_NONE;
554
555 for (i = 0; e; i++)
556 {
557 if (menuentry_eq (e->title, val)
558 || menuentry_eq (e->id, val))
559 {
560 entry = i;
561 break;
562 }
563 e = e->next;
564 }
565
566 if (! e)
567 entry = -1;
568 }
569
570 if (grub_errno != GRUB_ERR_NONE)
571 {
572 grub_errno = GRUB_ERR_NONE;
573 entry = -1;
574 }
575
576 grub_error_pop ();
577
578 return entry;
579 }
580
581 /* Check whether a second has elapsed since the last tick. If so, adjust
582 the timer and return 1; otherwise, return 0. */
583 static int
584 has_second_elapsed (grub_uint64_t *saved_time)
585 {
586 grub_uint64_t current_time;
587
588 current_time = grub_get_time_ms ();
589 if (current_time - *saved_time >= 1000)
590 {
591 *saved_time = current_time;
592 return 1;
593 }
594 else
595 return 0;
596 }
597
598 static void
599 print_countdown (struct grub_term_coordinate *pos, int n)
600 {
601 grub_term_restore_pos (pos);
602 /* NOTE: Do not remove the trailing space characters.
603 They are required to clear the line. */
604 grub_printf ("%d ", n);
605 grub_refresh ();
606 }
607
608 #define GRUB_MENU_PAGE_SIZE 10
609
610 /* Show the menu and handle menu entry selection. Returns the menu entry
611 index that should be executed or -1 if no entry should be executed (e.g.,
612 Esc pressed to exit a sub-menu or switching menu viewers).
613 If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
614 entry to be executed is a result of an automatic default selection because
615 of the timeout. */
616 static int
617 run_menu (grub_menu_t menu, int nested, int *auto_boot)
618 {
619 const char *cmdstr;
620 grub_uint64_t saved_time;
621 int default_entry,current_entry;
622 int timeout;
623 enum timeout_style timeout_style;
624
625 default_entry = get_entry_number (menu, "default");
626
627 if (g_ventoy_suppress_esc)
628 default_entry = 1;
629 else if (g_ventoy_last_entry >= 0 && g_ventoy_last_entry < menu->size) {
630 default_entry = g_ventoy_last_entry;
631 }
632 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
633 the first entry. */
634 else if (default_entry < 0 || default_entry >= menu->size)
635 default_entry = 0;
636
637 timeout = grub_menu_get_timeout ();
638 if (timeout < 0)
639 /* If there is no timeout, the "countdown" and "hidden" styles result in
640 the system doing nothing and providing no or very little indication
641 why. Technically this is what the user asked for, but it's not very
642 useful and likely to be a source of confusion, so we disallow this. */
643 grub_env_unset ("timeout_style");
644
645 timeout_style = get_timeout_style ();
646
647 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN
648 || timeout_style == TIMEOUT_STYLE_HIDDEN)
649 {
650 static struct grub_term_coordinate *pos;
651 int entry = -1;
652
653 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
654 {
655 pos = grub_term_save_pos ();
656 print_countdown (pos, timeout);
657 }
658
659 /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
660 or the timeout expires. */
661 saved_time = grub_get_time_ms ();
662 while (1)
663 {
664 int key;
665
666 key = grub_getkey_noblock ();
667 if (key != GRUB_TERM_NO_KEY)
668 {
669 entry = get_entry_index_by_hotkey (menu, key);
670 if (entry >= 0)
671 break;
672 }
673 if (key == GRUB_TERM_ESC)
674 {
675 timeout = -1;
676 break;
677 }
678
679 if (timeout > 0 && has_second_elapsed (&saved_time))
680 {
681 timeout--;
682 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
683 print_countdown (pos, timeout);
684 }
685
686 if (timeout == 0)
687 /* We will fall through to auto-booting the default entry. */
688 break;
689 }
690
691 grub_env_unset ("timeout");
692 grub_env_unset ("timeout_style");
693 if (entry >= 0)
694 {
695 *auto_boot = 0;
696 return entry;
697 }
698 }
699
700 /* If timeout is 0, drawing is pointless (and ugly). */
701 if (timeout == 0)
702 {
703 *auto_boot = 1;
704 return default_entry;
705 }
706
707 current_entry = default_entry;
708
709 refresh:
710 menu_set_chosen_tip(menu, current_entry);
711 menu_init (current_entry, menu, nested);
712
713 /* Initialize the time. */
714 saved_time = grub_get_time_ms ();
715
716 timeout = grub_menu_get_timeout ();
717
718 if (timeout > 0)
719 menu_print_timeout (timeout);
720 else
721 clear_timeout ();
722
723 while (1)
724 {
725 int c;
726 timeout = grub_menu_get_timeout ();
727
728 if (grub_normal_exit_level)
729 return -1;
730
731 if (timeout > 0 && has_second_elapsed (&saved_time))
732 {
733 timeout--;
734 grub_menu_set_timeout (timeout);
735 menu_print_timeout (timeout);
736 }
737
738 if (timeout == 0)
739 {
740 grub_env_unset ("timeout");
741 *auto_boot = 1;
742 menu_fini ();
743 return default_entry;
744 }
745
746 c = grub_getkey_noblock ();
747
748 /* Negative values are returned on error. */
749 if ((c != GRUB_TERM_NO_KEY) && (c > 0))
750 {
751 if (timeout >= 0)
752 {
753 grub_env_unset ("timeout");
754 grub_env_unset ("fallback");
755 clear_timeout ();
756 }
757
758 switch (c)
759 {
760 case GRUB_TERM_KEY_HOME:
761 case GRUB_TERM_CTRL | 'a':
762 current_entry = 0;
763 menu_set_chosen_entry (menu, current_entry);
764 break;
765
766 case GRUB_TERM_KEY_END:
767 case GRUB_TERM_CTRL | 'e':
768 current_entry = menu->size - 1;
769 menu_set_chosen_entry (menu, current_entry);
770 break;
771
772 case GRUB_TERM_KEY_UP:
773 case GRUB_TERM_CTRL | 'p':
774 case '^':
775 if (current_entry > 0)
776 current_entry--;
777 menu_set_chosen_entry (menu, current_entry);
778 break;
779
780 case GRUB_TERM_CTRL | 'n':
781 case GRUB_TERM_KEY_DOWN:
782 case 'v':
783 if (current_entry < menu->size - 1)
784 current_entry++;
785 menu_set_chosen_entry (menu, current_entry);
786 break;
787
788 case GRUB_TERM_CTRL | 'g':
789 case GRUB_TERM_KEY_PPAGE:
790 if (current_entry < GRUB_MENU_PAGE_SIZE)
791 current_entry = 0;
792 else
793 current_entry -= GRUB_MENU_PAGE_SIZE;
794 menu_set_chosen_entry (menu, current_entry);
795 break;
796
797 case GRUB_TERM_CTRL | 'c':
798 case GRUB_TERM_KEY_NPAGE:
799 if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
800 current_entry += GRUB_MENU_PAGE_SIZE;
801 else
802 current_entry = menu->size - 1;
803 menu_set_chosen_entry (menu, current_entry);
804 break;
805
806 case '\n':
807 case '\r':
808 // case GRUB_TERM_KEY_RIGHT:
809 case GRUB_TERM_CTRL | 'f':
810 menu_fini ();
811 *auto_boot = 0;
812 return current_entry;
813
814 case GRUB_TERM_ESC:
815 if (nested && 0 == g_ventoy_suppress_esc)
816 {
817 menu_fini ();
818 return -1;
819 }
820 break;
821
822 case 'c':
823 menu_fini ();
824 grub_cmdline_run (1, 0);
825 goto refresh;
826
827 case 'e':
828 menu_fini ();
829 {
830 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
831 if (e)
832 grub_menu_entry_run (e);
833 }
834 goto refresh;
835
836 case GRUB_TERM_KEY_F2:
837 case '2':
838 if (0 == g_ventoy_fn_mutex) {
839 cmdstr = grub_env_get("VTOY_F2_CMD");
840 if (cmdstr)
841 {
842 menu_fini ();
843 g_ventoy_fn_mutex = 1;
844 grub_script_execute_sourcecode(cmdstr);
845 g_ventoy_fn_mutex = 0;
846 goto refresh;
847 }
848 }
849 break;
850 case GRUB_TERM_KEY_F3:
851 case '3':
852 if (0 == g_ventoy_fn_mutex) {
853 cmdstr = grub_env_get("VTOY_F3_CMD");
854 if (cmdstr)
855 {
856 menu_fini ();
857 grub_script_execute_sourcecode(cmdstr);
858 goto refresh;
859 }
860 }
861 break;
862 case GRUB_TERM_KEY_F4:
863 case '4':
864 if (0 == g_ventoy_fn_mutex) {
865 cmdstr = grub_env_get("VTOY_F4_CMD");
866 if (cmdstr)
867 {
868 menu_fini ();
869 g_ventoy_fn_mutex = 1;
870 grub_script_execute_sourcecode(cmdstr);
871 g_ventoy_fn_mutex = 0;
872 goto refresh;
873 }
874 }
875 break;
876 case GRUB_TERM_KEY_F5:
877 case '5':
878 if (0 == g_ventoy_fn_mutex) {
879 cmdstr = grub_env_get("VTOY_F5_CMD");
880 if (cmdstr)
881 {
882 menu_fini ();
883 g_ventoy_fn_mutex = 1;
884 grub_script_execute_sourcecode(cmdstr);
885 g_ventoy_fn_mutex = 0;
886 goto refresh;
887 }
888 }
889 break;
890 case GRUB_TERM_KEY_F6:
891 case '6':
892 if (0 == g_ventoy_fn_mutex) {
893 cmdstr = grub_env_get("VTOY_F6_CMD");
894 if (cmdstr)
895 {
896 menu_fini ();
897 g_ventoy_fn_mutex = 1;
898 grub_script_execute_sourcecode(cmdstr);
899 g_ventoy_fn_mutex = 0;
900 goto refresh;
901 }
902 }
903 break;
904 case GRUB_TERM_KEY_F7:
905 menu_fini ();
906 if (g_ventoy_terminal_output == 0)
907 {
908 grub_script_execute_sourcecode("terminal_output console");
909 g_ventoy_terminal_output = 1;
910 }
911 else
912 {
913 grub_script_execute_sourcecode("terminal_output gfxterm");
914 g_ventoy_terminal_output = 0;
915 }
916 goto refresh;
917 case GRUB_TERM_KEY_F1:
918 case '1':
919 menu_fini ();
920 g_ventoy_memdisk_mode = 1 - g_ventoy_memdisk_mode;
921 g_ventoy_menu_refresh = 1;
922 goto refresh;
923
924 case (GRUB_TERM_CTRL | 'i'):
925 menu_fini ();
926 g_ventoy_iso_raw = 1 - g_ventoy_iso_raw;
927 g_ventoy_menu_refresh = 1;
928 goto refresh;
929
930 case (GRUB_TERM_CTRL | 'r'):
931 menu_fini ();
932 g_ventoy_grub2_mode = 1 - g_ventoy_grub2_mode;
933 g_ventoy_menu_refresh = 1;
934 goto refresh;
935
936 case (GRUB_TERM_CTRL | 'w'):
937 menu_fini ();
938 g_ventoy_wimboot_mode = 1 - g_ventoy_wimboot_mode;
939 g_ventoy_menu_refresh = 1;
940 goto refresh;
941
942 case (GRUB_TERM_CTRL | 'u'):
943 menu_fini ();
944 g_ventoy_iso_uefi_drv = 1 - g_ventoy_iso_uefi_drv;
945 g_ventoy_menu_refresh = 1;
946 goto refresh;
947
948 default:
949 {
950 int entry;
951
952 entry = get_entry_index_by_hotkey (menu, c);
953 if (entry >= 0)
954 {
955 menu_fini ();
956 *auto_boot = 0;
957 return entry;
958 }
959 }
960 break;
961 }
962 }
963 }
964
965 /* Never reach here. */
966 }
967
968 /* Callback invoked immediately before a menu entry is executed. */
969 static void
970 notify_booting (grub_menu_entry_t entry,
971 void *userdata __attribute__((unused)))
972 {
973 grub_printf (" ");
974 grub_printf_ (N_("Booting `%s'"), entry->title);
975 grub_printf ("\n\n");
976 }
977
978 /* Callback invoked when a default menu entry executed because of a timeout
979 has failed and an attempt will be made to execute the next fallback
980 entry, ENTRY. */
981 static void
982 notify_fallback (grub_menu_entry_t entry,
983 void *userdata __attribute__((unused)))
984 {
985 grub_printf ("\n ");
986 grub_printf_ (N_("Falling back to `%s'"), entry->title);
987 grub_printf ("\n\n");
988 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
989 }
990
991 /* Callback invoked when a menu entry has failed and there is no remaining
992 fallback entry to attempt. */
993 static void
994 notify_execution_failure (void *userdata __attribute__((unused)))
995 {
996 if (grub_errno != GRUB_ERR_NONE)
997 {
998 grub_print_error ();
999 grub_errno = GRUB_ERR_NONE;
1000 }
1001 grub_printf ("\n ");
1002 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
1003 grub_wait_after_message ();
1004 }
1005
1006 /* Callbacks used by the text menu to provide user feedback when menu entries
1007 are executed. */
1008 static struct grub_menu_execute_callback execution_callback =
1009 {
1010 .notify_booting = notify_booting,
1011 .notify_fallback = notify_fallback,
1012 .notify_failure = notify_execution_failure
1013 };
1014
1015 static grub_err_t
1016 show_menu (grub_menu_t menu, int nested, int autobooted)
1017 {
1018 const char *def;
1019 def = grub_env_get("VTOY_DEFAULT_IMAGE");
1020
1021 while (1)
1022 {
1023 int boot_entry;
1024 grub_menu_entry_t e;
1025 int auto_boot;
1026
1027 boot_entry = run_menu (menu, nested, &auto_boot);
1028 if (boot_entry < 0)
1029 break;
1030
1031 if (auto_boot && def && grub_strcmp(def, "VTOY_EXIT") == 0) {
1032 grub_exit();
1033 }
1034
1035 if (autobooted == 0 && auto_boot == 0) {
1036 g_ventoy_last_entry = boot_entry;
1037 if (g_ventoy_menu_esc)
1038 break;
1039 }
1040
1041 e = grub_menu_get_entry (menu, boot_entry);
1042 if (! e)
1043 continue; /* Menu is empty. */
1044
1045 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RET", 8) == 0)
1046 break;
1047
1048 grub_cls ();
1049
1050 if (auto_boot)
1051 grub_menu_execute_with_fallback (menu, e, autobooted,
1052 &execution_callback, 0);
1053 else
1054 grub_menu_execute_entry (e, 0);
1055 if (autobooted)
1056 break;
1057
1058 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RUN_RET", 12) == 0)
1059 break;
1060 }
1061
1062 return GRUB_ERR_NONE;
1063 }
1064
1065 grub_err_t
1066 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
1067 {
1068 grub_err_t err1, err2;
1069
1070 while (1)
1071 {
1072 err1 = show_menu (menu, nested, autoboot);
1073 autoboot = 0;
1074 grub_print_error ();
1075
1076 if (grub_normal_exit_level)
1077 break;
1078
1079 err2 = grub_auth_check_authentication (NULL);
1080 if (err2)
1081 {
1082 grub_print_error ();
1083 grub_errno = GRUB_ERR_NONE;
1084 continue;
1085 }
1086
1087 break;
1088 }
1089
1090 return err1;
1091 }