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