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>
36 /* Time to delay after displaying an error message about a default/fallback
37 entry failing to boot. */
38 #define DEFAULT_ENTRY_ERROR_DELAY_MS 2500
40 grub_err_t (*grub_gfxmenu_try_hook
) (int entry
, grub_menu_t menu
,
45 TIMEOUT_STYLE_COUNTDOWN
,
49 struct timeout_style_name
{
51 enum timeout_style style
;
52 } timeout_style_names
[] = {
53 {"menu", TIMEOUT_STYLE_MENU
},
54 {"countdown", TIMEOUT_STYLE_COUNTDOWN
},
55 {"hidden", TIMEOUT_STYLE_HIDDEN
},
59 /* Wait until the user pushes any key so that the user
60 can see what happened. */
62 grub_wait_after_message (void)
64 grub_uint64_t endtime
;
66 grub_printf_ (N_("Press any key to continue..."));
69 endtime
= grub_get_time_ms () + 10000;
71 while (grub_get_time_ms () < endtime
72 && grub_getkey_noblock () == GRUB_TERM_NO_KEY
);
77 /* Get a menu entry by its index in the entry list. */
79 grub_menu_get_entry (grub_menu_t menu
, int no
)
83 for (e
= menu
->entry_list
; e
&& no
> 0; e
= e
->next
, no
--)
89 /* Get the index of a menu entry associated with a given hotkey, or -1. */
91 get_entry_index_by_hotkey (grub_menu_t menu
, int hotkey
)
93 grub_menu_entry_t entry
;
96 for (i
= 0, entry
= menu
->entry_list
; i
< menu
->size
;
97 i
++, entry
= entry
->next
)
98 if (entry
->hotkey
== hotkey
)
104 /* Return the timeout style. If the variable "timeout_style" is not set or
105 invalid, default to TIMEOUT_STYLE_MENU. */
106 static enum timeout_style
107 get_timeout_style (void)
110 struct timeout_style_name
*style_name
;
112 val
= grub_env_get ("timeout_style");
114 return TIMEOUT_STYLE_MENU
;
116 for (style_name
= timeout_style_names
; style_name
->name
; style_name
++)
117 if (grub_strcmp (style_name
->name
, val
) == 0)
118 return style_name
->style
;
120 return TIMEOUT_STYLE_MENU
;
123 /* Return the current timeout. If the variable "timeout" is not set or
124 invalid, return -1. */
126 grub_menu_get_timeout (void)
131 val
= grub_env_get ("timeout");
137 timeout
= (int) grub_strtoul (val
, 0, 0);
139 /* If the value is invalid, unset the variable. */
140 if (grub_errno
!= GRUB_ERR_NONE
)
142 grub_env_unset ("timeout");
143 grub_errno
= GRUB_ERR_NONE
;
152 /* Set current timeout in the variable "timeout". */
154 grub_menu_set_timeout (int timeout
)
156 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
161 grub_snprintf (buf
, sizeof (buf
), "%d", timeout
);
162 grub_env_set ("timeout", buf
);
166 /* Get the first entry number from the value of the environment variable NAME,
167 which is a space-separated list of non-negative integers. The entry number
168 which is returned is stripped from the value of NAME. If no entry number
169 can be found, -1 is returned. */
171 get_and_remove_first_entry_number (const char *name
)
177 val
= grub_env_get (name
);
183 entry
= (int) grub_strtoul (val
, &tail
, 0);
185 if (grub_errno
== GRUB_ERR_NONE
)
187 /* Skip whitespace to find the next digit. */
188 while (*tail
&& grub_isspace (*tail
))
190 grub_env_set (name
, tail
);
194 grub_env_unset (name
);
195 grub_errno
= GRUB_ERR_NONE
;
204 /* Run a menu entry. */
206 grub_menu_execute_entry(grub_menu_entry_t entry
, int auto_boot
)
208 grub_err_t err
= GRUB_ERR_NONE
;
210 grub_menu_t menu
= NULL
;
211 char *optr
, *buf
, *oldchosen
= NULL
, *olddefault
= NULL
;
212 const char *ptr
, *chosen
, *def
;
215 if (entry
->restricted
)
216 err
= grub_auth_check_authentication (entry
->users
);
221 grub_errno
= GRUB_ERR_NONE
;
225 errs_before
= grub_err_printed_errors
;
227 chosen
= grub_env_get ("chosen");
228 def
= grub_env_get ("default");
232 grub_env_context_open ();
233 menu
= grub_zalloc (sizeof (*menu
));
236 grub_env_set_menu (menu
);
238 grub_env_set ("timeout", "0");
241 for (ptr
= entry
->id
; *ptr
; ptr
++)
242 sz
+= (*ptr
== '>') ? 2 : 1;
245 oldchosen
= grub_strdup (chosen
);
251 olddefault
= grub_strdup (def
);
257 sz
+= grub_strlen (chosen
);
259 buf
= grub_malloc (sz
);
267 optr
= grub_stpcpy (optr
, chosen
);
270 for (ptr
= entry
->id
; *ptr
; ptr
++)
277 grub_env_set ("chosen", buf
);
278 grub_env_export ("chosen");
282 for (ptr
= def
; ptr
&& *ptr
; ptr
++)
284 if (ptr
[0] == '>' && ptr
[1] == '>')
293 if (ptr
&& ptr
[0] && ptr
[1])
294 grub_env_set ("default", ptr
+ 1);
296 grub_env_unset ("default");
298 grub_script_execute_new_scope (entry
->sourcecode
, entry
->argc
, entry
->args
);
300 if (errs_before
!= grub_err_printed_errors
)
301 grub_wait_after_message ();
303 errs_before
= grub_err_printed_errors
;
305 if (grub_errno
== GRUB_ERR_NONE
&& grub_loader_is_loaded ())
306 /* Implicit execution of boot, only if something is loaded. */
307 grub_command_execute ("boot", 0, 0);
309 if (errs_before
!= grub_err_printed_errors
)
310 grub_wait_after_message ();
314 if (menu
&& menu
->size
)
316 grub_show_menu (menu
, 1, auto_boot
);
317 grub_normal_free_menu (menu
);
319 grub_env_context_close ();
322 grub_env_set ("chosen", oldchosen
);
324 grub_env_unset ("chosen");
326 grub_env_set ("default", olddefault
);
328 grub_env_unset ("default");
329 grub_env_unset ("timeout");
332 /* Execute ENTRY from the menu MENU, falling back to entries specified
333 in the environment variable "fallback" if it fails. CALLBACK is a
334 pointer to a struct of function pointers which are used to allow the
335 caller provide feedback to the user. */
337 grub_menu_execute_with_fallback (grub_menu_t menu
,
338 grub_menu_entry_t entry
,
340 grub_menu_execute_callback_t callback
,
345 callback
->notify_booting (entry
, callback_data
);
347 grub_menu_execute_entry (entry
, 1);
349 /* Deal with fallback entries. */
350 while ((fallback_entry
= get_and_remove_first_entry_number ("fallback"))
354 grub_errno
= GRUB_ERR_NONE
;
356 entry
= grub_menu_get_entry (menu
, fallback_entry
);
357 callback
->notify_fallback (entry
, callback_data
);
358 grub_menu_execute_entry (entry
, 1);
359 /* If the function call to execute the entry returns at all, then this is
360 taken to indicate a boot failure. For menu entries that do something
361 other than actually boot an operating system, this could assume
362 incorrectly that something failed. */
366 callback
->notify_failure (callback_data
);
369 static struct grub_menu_viewer
*viewers
;
372 menu_set_chosen_entry (int entry
)
374 struct grub_menu_viewer
*cur
;
375 for (cur
= viewers
; cur
; cur
= cur
->next
)
376 cur
->set_chosen_entry (entry
, cur
->data
);
380 menu_print_timeout (int timeout
)
382 struct grub_menu_viewer
*cur
;
383 for (cur
= viewers
; cur
; cur
= cur
->next
)
384 cur
->print_timeout (timeout
, cur
->data
);
390 struct grub_menu_viewer
*cur
, *next
;
391 for (cur
= viewers
; cur
; cur
= next
)
394 cur
->fini (cur
->data
);
401 menu_init (int entry
, grub_menu_t menu
, int nested
)
403 struct grub_term_output
*term
;
406 FOR_ACTIVE_TERM_OUTPUTS(term
)
407 if (term
->fullscreen
)
409 if (grub_env_get ("theme"))
411 if (!grub_gfxmenu_try_hook
)
413 grub_dl_load ("gfxmenu");
416 if (grub_gfxmenu_try_hook
)
419 err
= grub_gfxmenu_try_hook (entry
, menu
, nested
);
427 grub_error (GRUB_ERR_BAD_MODULE
,
428 N_("module `%s' isn't loaded"),
431 grub_wait_after_message ();
433 grub_errno
= GRUB_ERR_NONE
;
438 FOR_ACTIVE_TERM_OUTPUTS(term
)
442 if (grub_strcmp (term
->name
, "gfxterm") == 0 && gfxmenu
)
445 err
= grub_menu_try_text (term
, entry
, menu
, nested
);
449 grub_errno
= GRUB_ERR_NONE
;
456 struct grub_menu_viewer
*cur
;
457 for (cur
= viewers
; cur
; cur
= cur
->next
)
458 cur
->clear_timeout (cur
->data
);
462 grub_menu_register_viewer (struct grub_menu_viewer
*viewer
)
464 viewer
->next
= viewers
;
469 menuentry_eq (const char *id
, const char *spec
)
471 const char *ptr1
, *ptr2
;
476 if (*ptr2
== '>' && ptr2
[1] != '>' && *ptr1
== 0)
478 if (*ptr2
== '>' && ptr2
[1] != '>')
492 /* Get the entry number from the variable NAME. */
494 get_entry_number (grub_menu_t menu
, const char *name
)
499 val
= grub_env_get (name
);
505 entry
= (int) grub_strtoul (val
, 0, 0);
507 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
509 /* See if the variable matches the title of a menu entry. */
510 grub_menu_entry_t e
= menu
->entry_list
;
513 grub_errno
= GRUB_ERR_NONE
;
517 if (menuentry_eq (e
->title
, val
)
518 || menuentry_eq (e
->id
, val
))
530 if (grub_errno
!= GRUB_ERR_NONE
)
532 grub_errno
= GRUB_ERR_NONE
;
541 /* Check whether a second has elapsed since the last tick. If so, adjust
542 the timer and return 1; otherwise, return 0. */
544 has_second_elapsed (grub_uint64_t
*saved_time
)
546 grub_uint64_t current_time
;
548 current_time
= grub_get_time_ms ();
549 if (current_time
- *saved_time
>= 1000)
551 *saved_time
= current_time
;
559 print_countdown (struct grub_term_coordinate
*pos
, int n
)
561 grub_term_restore_pos (pos
);
562 /* NOTE: Do not remove the trailing space characters.
563 They are required to clear the line. */
564 grub_printf ("%d ", n
);
568 #define GRUB_MENU_PAGE_SIZE 10
570 /* Show the menu and handle menu entry selection. Returns the menu entry
571 index that should be executed or -1 if no entry should be executed (e.g.,
572 Esc pressed to exit a sub-menu or switching menu viewers).
573 If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
574 entry to be executed is a result of an automatic default selection because
577 run_menu (grub_menu_t menu
, int nested
, int *auto_boot
)
579 grub_uint64_t saved_time
;
580 int default_entry
, current_entry
;
582 enum timeout_style timeout_style
;
584 default_entry
= get_entry_number (menu
, "default");
586 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
588 if (default_entry
< 0 || default_entry
>= menu
->size
)
591 timeout
= grub_menu_get_timeout ();
593 /* If there is no timeout, the "countdown" and "hidden" styles result in
594 the system doing nothing and providing no or very little indication
595 why. Technically this is what the user asked for, but it's not very
596 useful and likely to be a source of confusion, so we disallow this. */
597 grub_env_unset ("timeout_style");
599 timeout_style
= get_timeout_style ();
601 if (timeout_style
== TIMEOUT_STYLE_COUNTDOWN
602 || timeout_style
== TIMEOUT_STYLE_HIDDEN
)
604 static struct grub_term_coordinate
*pos
;
607 if (timeout_style
== TIMEOUT_STYLE_COUNTDOWN
&& timeout
)
609 pos
= grub_term_save_pos ();
610 print_countdown (pos
, timeout
);
613 /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
614 or the timeout expires. */
615 saved_time
= grub_get_time_ms ();
619 grub_term_input_t term
;
622 if (grub_term_poll_usb
)
623 grub_term_poll_usb (0);
625 FOR_ACTIVE_TERM_INPUTS(term
)
627 if (term
->getkeystatus
)
628 mods
|= term
->getkeystatus (term
);
632 (mods
& (GRUB_TERM_STATUS_LSHIFT
633 | GRUB_TERM_STATUS_RSHIFT
)) != 0)
639 key
= grub_getkey_noblock ();
640 if (key
!= GRUB_TERM_NO_KEY
)
642 entry
= get_entry_index_by_hotkey (menu
, key
);
646 if (key
== GRUB_TERM_ESC
)
652 if (timeout
> 0 && has_second_elapsed (&saved_time
))
655 if (timeout_style
== TIMEOUT_STYLE_COUNTDOWN
)
656 print_countdown (pos
, timeout
);
660 /* We will fall through to auto-booting the default entry. */
664 grub_env_unset ("timeout");
665 grub_env_unset ("timeout_style");
673 /* If timeout is 0, drawing is pointless (and ugly). */
677 return default_entry
;
680 current_entry
= default_entry
;
683 menu_init (current_entry
, menu
, nested
);
685 /* Initialize the time. */
686 saved_time
= grub_get_time_ms ();
688 timeout
= grub_menu_get_timeout ();
691 menu_print_timeout (timeout
);
698 timeout
= grub_menu_get_timeout ();
700 if (grub_normal_exit_level
)
703 if (timeout
> 0 && has_second_elapsed (&saved_time
))
706 grub_menu_set_timeout (timeout
);
707 menu_print_timeout (timeout
);
712 grub_env_unset ("timeout");
715 return default_entry
;
718 c
= grub_getkey_noblock ();
720 if (c
!= GRUB_TERM_NO_KEY
)
724 grub_env_unset ("timeout");
725 grub_env_unset ("fallback");
731 case GRUB_TERM_KEY_HOME
:
732 case GRUB_TERM_CTRL
| 'a':
734 menu_set_chosen_entry (current_entry
);
737 case GRUB_TERM_KEY_END
:
738 case GRUB_TERM_CTRL
| 'e':
739 current_entry
= menu
->size
- 1;
740 menu_set_chosen_entry (current_entry
);
743 case GRUB_TERM_KEY_UP
:
744 case GRUB_TERM_CTRL
| 'p':
746 if (current_entry
> 0)
748 menu_set_chosen_entry (current_entry
);
751 case GRUB_TERM_CTRL
| 'n':
752 case GRUB_TERM_KEY_DOWN
:
754 if (current_entry
< menu
->size
- 1)
756 menu_set_chosen_entry (current_entry
);
759 case GRUB_TERM_CTRL
| 'g':
760 case GRUB_TERM_KEY_PPAGE
:
761 if (current_entry
< GRUB_MENU_PAGE_SIZE
)
764 current_entry
-= GRUB_MENU_PAGE_SIZE
;
765 menu_set_chosen_entry (current_entry
);
768 case GRUB_TERM_CTRL
| 'c':
769 case GRUB_TERM_KEY_NPAGE
:
770 if (current_entry
+ GRUB_MENU_PAGE_SIZE
< menu
->size
)
771 current_entry
+= GRUB_MENU_PAGE_SIZE
;
773 current_entry
= menu
->size
- 1;
774 menu_set_chosen_entry (current_entry
);
779 case GRUB_TERM_KEY_RIGHT
:
780 case GRUB_TERM_CTRL
| 'f':
783 return current_entry
;
795 grub_cmdline_run (1);
801 grub_menu_entry_t e
= grub_menu_get_entry (menu
, current_entry
);
803 grub_menu_entry_run (e
);
811 entry
= get_entry_index_by_hotkey (menu
, c
);
824 /* Never reach here. */
827 /* Callback invoked immediately before a menu entry is executed. */
829 notify_booting (grub_menu_entry_t entry
831 __attribute__((unused
))
834 void *userdata
__attribute__((unused
)))
838 grub_printf_ (N_("Booting `%s'"), entry
->title
);
839 grub_printf ("\n\n");
843 /* Callback invoked when a default menu entry executed because of a timeout
844 has failed and an attempt will be made to execute the next fallback
847 notify_fallback (grub_menu_entry_t entry
,
848 void *userdata
__attribute__((unused
)))
851 grub_printf_ (N_("Falling back to `%s'"), entry
->title
);
852 grub_printf ("\n\n");
853 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS
);
856 /* Callback invoked when a menu entry has failed and there is no remaining
857 fallback entry to attempt. */
859 notify_execution_failure (void *userdata
__attribute__((unused
)))
861 if (grub_errno
!= GRUB_ERR_NONE
)
864 grub_errno
= GRUB_ERR_NONE
;
867 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
868 grub_wait_after_message ();
871 /* Callbacks used by the text menu to provide user feedback when menu entries
873 static struct grub_menu_execute_callback execution_callback
=
875 .notify_booting
= notify_booting
,
876 .notify_fallback
= notify_fallback
,
877 .notify_failure
= notify_execution_failure
881 show_menu (grub_menu_t menu
, int nested
, int autobooted
)
889 int initial_timeout
= grub_menu_get_timeout ();
892 boot_entry
= run_menu (menu
, nested
, &auto_boot
);
896 e
= grub_menu_get_entry (menu
, boot_entry
);
898 continue; /* Menu is empty. */
901 /* Only clear the screen if we drew the menu in the first place. */
902 if (initial_timeout
!= 0)
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
;