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