1 /* menu.c - General supporting functionality for menus. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/normal.h>
21 #include <grub/misc.h>
22 #include <grub/loader.h>
24 #include <grub/time.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>
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
41 grub_err_t (*grub_gfxmenu_try_hook
) (int entry
, grub_menu_t menu
,
46 TIMEOUT_STYLE_COUNTDOWN
,
50 struct timeout_style_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
},
60 /* Wait until the user pushes any key so that the user
61 can see what happened. */
63 grub_wait_after_message (void)
65 grub_uint64_t endtime
;
67 grub_printf_ (N_("Press any key to continue..."));
70 endtime
= grub_get_time_ms () + 10000;
72 while (grub_get_time_ms () < endtime
73 && grub_getkey_noblock () == GRUB_TERM_NO_KEY
);
78 /* Get a menu entry by its index in the entry list. */
80 grub_menu_get_entry (grub_menu_t menu
, int no
)
84 for (e
= menu
->entry_list
; e
&& no
> 0; e
= e
->next
, no
--)
90 /* Get the index of a menu entry associated with a given hotkey, or -1. */
92 get_entry_index_by_hotkey (grub_menu_t menu
, int hotkey
)
94 grub_menu_entry_t entry
;
97 for (i
= 0, entry
= menu
->entry_list
; i
< menu
->size
;
98 i
++, entry
= entry
->next
)
99 if (entry
->hotkey
== hotkey
)
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)
111 struct timeout_style_name
*style_name
;
113 val
= grub_env_get ("timeout_style");
115 return TIMEOUT_STYLE_MENU
;
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
;
121 return TIMEOUT_STYLE_MENU
;
124 /* Return the current timeout. If the variable "timeout" is not set or
125 invalid, return -1. */
127 grub_menu_get_timeout (void)
132 val
= grub_env_get ("timeout");
138 timeout
= (int) grub_strtoul (val
, 0, 0);
140 /* If the value is invalid, unset the variable. */
141 if (grub_errno
!= GRUB_ERR_NONE
)
143 grub_env_unset ("timeout");
144 grub_errno
= GRUB_ERR_NONE
;
153 /* Set current timeout in the variable "timeout". */
155 grub_menu_set_timeout (int timeout
)
157 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
162 grub_snprintf (buf
, sizeof (buf
), "%d", timeout
);
163 grub_env_set ("timeout", buf
);
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. */
172 get_and_remove_first_entry_number (const char *name
)
178 val
= grub_env_get (name
);
184 entry
= (int) grub_strtoul (val
, &tail
, 0);
186 if (grub_errno
== GRUB_ERR_NONE
)
188 /* Skip whitespace to find the next digit. */
189 while (*tail
&& grub_isspace (*tail
))
191 grub_env_set (name
, tail
);
195 grub_env_unset (name
);
196 grub_errno
= GRUB_ERR_NONE
;
205 /* Run a menu entry. */
207 grub_menu_execute_entry(grub_menu_entry_t entry
, int auto_boot
)
209 grub_err_t err
= GRUB_ERR_NONE
;
211 grub_menu_t menu
= NULL
;
212 char *optr
, *buf
, *oldchosen
= NULL
, *olddefault
= NULL
;
213 const char *ptr
, *chosen
, *def
;
216 if (entry
->restricted
)
217 err
= grub_auth_check_authentication (entry
->users
);
222 grub_errno
= GRUB_ERR_NONE
;
226 errs_before
= grub_err_printed_errors
;
228 chosen
= grub_env_get ("chosen");
229 def
= grub_env_get ("default");
233 grub_env_context_open ();
234 menu
= grub_zalloc (sizeof (*menu
));
237 grub_env_set_menu (menu
);
239 grub_env_set ("timeout", "0");
242 for (ptr
= entry
->id
; *ptr
; ptr
++)
243 sz
+= (*ptr
== '>') ? 2 : 1;
246 oldchosen
= grub_strdup (chosen
);
252 olddefault
= grub_strdup (def
);
258 sz
+= grub_strlen (chosen
);
260 buf
= grub_malloc (sz
);
268 optr
= grub_stpcpy (optr
, chosen
);
271 for (ptr
= entry
->id
; *ptr
; ptr
++)
278 grub_env_set ("chosen", buf
);
279 grub_env_export ("chosen");
283 for (ptr
= def
; ptr
&& *ptr
; ptr
++)
285 if (ptr
[0] == '>' && ptr
[1] == '>')
294 if (ptr
&& ptr
[0] && ptr
[1])
295 grub_env_set ("default", ptr
+ 1);
297 grub_env_unset ("default");
299 grub_script_execute_new_scope (entry
->sourcecode
, entry
->argc
, entry
->args
);
301 if (errs_before
!= grub_err_printed_errors
)
302 grub_wait_after_message ();
304 errs_before
= grub_err_printed_errors
;
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);
310 if (errs_before
!= grub_err_printed_errors
)
311 grub_wait_after_message ();
315 if (menu
&& menu
->size
)
317 grub_show_menu (menu
, 1, auto_boot
);
318 grub_normal_free_menu (menu
);
320 grub_env_context_close ();
323 grub_env_set ("chosen", oldchosen
);
325 grub_env_unset ("chosen");
327 grub_env_set ("default", olddefault
);
329 grub_env_unset ("default");
330 grub_env_unset ("timeout");
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. */
338 grub_menu_execute_with_fallback (grub_menu_t menu
,
339 grub_menu_entry_t entry
,
341 grub_menu_execute_callback_t callback
,
346 callback
->notify_booting (entry
, callback_data
);
348 grub_menu_execute_entry (entry
, 1);
350 /* Deal with fallback entries. */
351 while ((fallback_entry
= get_and_remove_first_entry_number ("fallback"))
355 grub_errno
= GRUB_ERR_NONE
;
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. */
367 callback
->notify_failure (callback_data
);
370 static struct grub_menu_viewer
*viewers
;
373 menu_set_chosen_entry (int entry
)
375 struct grub_menu_viewer
*cur
;
376 for (cur
= viewers
; cur
; cur
= cur
->next
)
377 cur
->set_chosen_entry (entry
, cur
->data
);
381 menu_print_timeout (int timeout
)
383 struct grub_menu_viewer
*cur
;
384 for (cur
= viewers
; cur
; cur
= cur
->next
)
385 cur
->print_timeout (timeout
, cur
->data
);
391 struct grub_menu_viewer
*cur
, *next
;
392 for (cur
= viewers
; cur
; cur
= next
)
395 cur
->fini (cur
->data
);
402 menu_init (int entry
, grub_menu_t menu
, int nested
)
404 struct grub_term_output
*term
;
407 FOR_ACTIVE_TERM_OUTPUTS(term
)
408 if (term
->fullscreen
)
410 if (grub_env_get ("theme"))
412 if (!grub_gfxmenu_try_hook
)
414 grub_dl_load ("gfxmenu");
417 if (grub_gfxmenu_try_hook
)
420 err
= grub_gfxmenu_try_hook (entry
, menu
, nested
);
428 grub_error (GRUB_ERR_BAD_MODULE
,
429 N_("module `%s' isn't loaded"),
432 grub_wait_after_message ();
434 grub_errno
= GRUB_ERR_NONE
;
439 FOR_ACTIVE_TERM_OUTPUTS(term
)
443 if (grub_strcmp (term
->name
, "gfxterm") == 0 && gfxmenu
)
446 err
= grub_menu_try_text (term
, entry
, menu
, nested
);
450 grub_errno
= GRUB_ERR_NONE
;
457 struct grub_menu_viewer
*cur
;
458 for (cur
= viewers
; cur
; cur
= cur
->next
)
459 cur
->clear_timeout (cur
->data
);
463 grub_menu_register_viewer (struct grub_menu_viewer
*viewer
)
465 viewer
->next
= viewers
;
470 menuentry_eq (const char *id
, const char *spec
)
472 const char *ptr1
, *ptr2
;
477 if (*ptr2
== '>' && ptr2
[1] != '>' && *ptr1
== 0)
479 if (*ptr2
== '>' && ptr2
[1] != '>')
493 /* Get the entry number from the variable NAME. */
495 get_entry_number (grub_menu_t menu
, const char *name
)
500 val
= grub_env_get (name
);
506 entry
= (int) grub_strtoul (val
, 0, 0);
508 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
510 /* See if the variable matches the title of a menu entry. */
511 grub_menu_entry_t e
= menu
->entry_list
;
514 grub_errno
= GRUB_ERR_NONE
;
518 if (menuentry_eq (e
->title
, val
)
519 || menuentry_eq (e
->id
, val
))
531 if (grub_errno
!= GRUB_ERR_NONE
)
533 grub_errno
= GRUB_ERR_NONE
;
542 /* Check whether a second has elapsed since the last tick. If so, adjust
543 the timer and return 1; otherwise, return 0. */
545 has_second_elapsed (grub_uint64_t
*saved_time
)
547 grub_uint64_t current_time
;
549 current_time
= grub_get_time_ms ();
550 if (current_time
- *saved_time
>= 1000)
552 *saved_time
= current_time
;
560 print_countdown (struct grub_term_coordinate
*pos
, int n
)
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
);
569 #define GRUB_MENU_PAGE_SIZE 10
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
578 run_menu (grub_menu_t menu
, int nested
, int *auto_boot
)
580 grub_uint64_t saved_time
;
581 int default_entry
, current_entry
;
583 enum timeout_style timeout_style
;
585 default_entry
= get_entry_number (menu
, "default");
587 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
589 if (default_entry
< 0 || default_entry
>= menu
->size
)
592 timeout
= grub_menu_get_timeout ();
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");
600 timeout_style
= get_timeout_style ();
602 if (timeout_style
== TIMEOUT_STYLE_COUNTDOWN
603 || timeout_style
== TIMEOUT_STYLE_HIDDEN
)
605 static struct grub_term_coordinate
*pos
;
608 if (timeout_style
== TIMEOUT_STYLE_COUNTDOWN
&& timeout
)
610 pos
= grub_term_save_pos ();
611 print_countdown (pos
, timeout
);
614 /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
615 or the timeout expires. */
616 saved_time
= grub_get_time_ms ();
621 key
= grub_getkey_noblock ();
622 if (key
!= GRUB_TERM_NO_KEY
)
624 entry
= get_entry_index_by_hotkey (menu
, key
);
628 if (key
== GRUB_TERM_ESC
)
634 if (timeout
> 0 && has_second_elapsed (&saved_time
))
637 if (timeout_style
== TIMEOUT_STYLE_COUNTDOWN
)
638 print_countdown (pos
, timeout
);
642 /* We will fall through to auto-booting the default entry. */
646 grub_env_unset ("timeout");
647 grub_env_unset ("timeout_style");
655 /* If timeout is 0, drawing is pointless (and ugly). */
659 return default_entry
;
662 current_entry
= default_entry
;
665 menu_init (current_entry
, menu
, nested
);
667 /* Initialize the time. */
668 saved_time
= grub_get_time_ms ();
670 timeout
= grub_menu_get_timeout ();
673 menu_print_timeout (timeout
);
680 timeout
= grub_menu_get_timeout ();
682 if (grub_normal_exit_level
)
685 if (timeout
> 0 && has_second_elapsed (&saved_time
))
688 grub_menu_set_timeout (timeout
);
689 menu_print_timeout (timeout
);
694 grub_env_unset ("timeout");
697 return default_entry
;
700 c
= grub_getkey_noblock ();
702 /* Negative values are returned on error. */
703 if ((c
!= GRUB_TERM_NO_KEY
) && (c
> 0))
707 grub_env_unset ("timeout");
708 grub_env_unset ("fallback");
714 case GRUB_TERM_KEY_HOME
:
715 case GRUB_TERM_CTRL
| 'a':
717 menu_set_chosen_entry (current_entry
);
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
);
726 case GRUB_TERM_KEY_UP
:
727 case GRUB_TERM_CTRL
| 'p':
729 if (current_entry
> 0)
731 menu_set_chosen_entry (current_entry
);
734 case GRUB_TERM_CTRL
| 'n':
735 case GRUB_TERM_KEY_DOWN
:
737 if (current_entry
< menu
->size
- 1)
739 menu_set_chosen_entry (current_entry
);
742 case GRUB_TERM_CTRL
| 'g':
743 case GRUB_TERM_KEY_PPAGE
:
744 if (current_entry
< GRUB_MENU_PAGE_SIZE
)
747 current_entry
-= GRUB_MENU_PAGE_SIZE
;
748 menu_set_chosen_entry (current_entry
);
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
;
756 current_entry
= menu
->size
- 1;
757 menu_set_chosen_entry (current_entry
);
762 // case GRUB_TERM_KEY_RIGHT:
763 case GRUB_TERM_CTRL
| 'f':
766 return current_entry
;
778 grub_cmdline_run (1, 0);
784 grub_menu_entry_t e
= grub_menu_get_entry (menu
, current_entry
);
786 grub_menu_entry_run (e
);
790 case GRUB_TERM_KEY_F1
:
792 if (grub_env_get("VTOY_MEM_DISK")) {
793 grub_env_unset("VTOY_MEM_DISK");
795 grub_env_set("VTOY_MEM_DISK", grub_env_get("VTOY_MEM_DISK_STR"));
797 grub_env_set("VTOY_MENU_REFRESH", "1");
800 case GRUB_TERM_KEY_F3
:
802 if (grub_env_get("VTOY_ISO_RAW")) {
803 grub_env_unset("VTOY_ISO_RAW");
805 grub_env_set("VTOY_ISO_RAW", grub_env_get("VTOY_ISO_RAW_STR"));
807 grub_env_set("VTOY_MENU_REFRESH", "1");
810 case GRUB_TERM_KEY_F4
:
812 if (grub_env_get("VTOY_ISO_UEFI_DRV")) {
813 grub_env_unset("VTOY_ISO_UEFI_DRV");
815 grub_env_set("VTOY_ISO_UEFI_DRV", grub_env_get("VTOY_ISO_UEFI_DRV_STR"));
817 grub_env_set("VTOY_MENU_REFRESH", "1");
824 entry
= get_entry_index_by_hotkey (menu
, c
);
837 /* Never reach here. */
840 /* Callback invoked immediately before a menu entry is executed. */
842 notify_booting (grub_menu_entry_t entry
,
843 void *userdata
__attribute__((unused
)))
846 grub_printf_ (N_("Booting `%s'"), entry
->title
);
847 grub_printf ("\n\n");
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
854 notify_fallback (grub_menu_entry_t entry
,
855 void *userdata
__attribute__((unused
)))
858 grub_printf_ (N_("Falling back to `%s'"), entry
->title
);
859 grub_printf ("\n\n");
860 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS
);
863 /* Callback invoked when a menu entry has failed and there is no remaining
864 fallback entry to attempt. */
866 notify_execution_failure (void *userdata
__attribute__((unused
)))
868 if (grub_errno
!= GRUB_ERR_NONE
)
871 grub_errno
= GRUB_ERR_NONE
;
874 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
875 grub_wait_after_message ();
878 /* Callbacks used by the text menu to provide user feedback when menu entries
880 static struct grub_menu_execute_callback execution_callback
=
882 .notify_booting
= notify_booting
,
883 .notify_fallback
= notify_fallback
,
884 .notify_failure
= notify_execution_failure
888 show_menu (grub_menu_t menu
, int nested
, int autobooted
)
896 boot_entry
= run_menu (menu
, nested
, &auto_boot
);
900 e
= grub_menu_get_entry (menu
, boot_entry
);
902 continue; /* Menu is empty. */
907 grub_menu_execute_with_fallback (menu
, e
, autobooted
,
908 &execution_callback
, 0);
910 grub_menu_execute_entry (e
, 0);
915 return GRUB_ERR_NONE
;
919 grub_show_menu (grub_menu_t menu
, int nested
, int autoboot
)
921 grub_err_t err1
, err2
;
925 err1
= show_menu (menu
, nested
, autoboot
);
929 if (grub_normal_exit_level
)
932 err2
= grub_auth_check_authentication (NULL
);
936 grub_errno
= GRUB_ERR_NONE
;