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
,
43 /* Wait until the user pushes any key so that the user
44 can see what happened. */
46 grub_wait_after_message (void)
48 grub_uint64_t endtime
;
50 grub_printf_ (N_("Press any key to continue..."));
53 endtime
= grub_get_time_ms () + 10000;
55 while (grub_get_time_ms () < endtime
56 && grub_getkey_noblock () == GRUB_TERM_NO_KEY
);
61 /* Get a menu entry by its index in the entry list. */
63 grub_menu_get_entry (grub_menu_t menu
, int no
)
67 for (e
= menu
->entry_list
; e
&& no
> 0; e
= e
->next
, no
--)
73 /* Return the current timeout. If the variable "timeout" is not set or
74 invalid, return -1. */
76 grub_menu_get_timeout (void)
81 val
= grub_env_get ("timeout");
87 timeout
= (int) grub_strtoul (val
, 0, 0);
89 /* If the value is invalid, unset the variable. */
90 if (grub_errno
!= GRUB_ERR_NONE
)
92 grub_env_unset ("timeout");
93 grub_errno
= GRUB_ERR_NONE
;
102 /* Set current timeout in the variable "timeout". */
104 grub_menu_set_timeout (int timeout
)
106 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
111 grub_snprintf (buf
, sizeof (buf
), "%d", timeout
);
112 grub_env_set ("timeout", buf
);
116 /* Get the first entry number from the value of the environment variable NAME,
117 which is a space-separated list of non-negative integers. The entry number
118 which is returned is stripped from the value of NAME. If no entry number
119 can be found, -1 is returned. */
121 get_and_remove_first_entry_number (const char *name
)
127 val
= grub_env_get (name
);
133 entry
= (int) grub_strtoul (val
, &tail
, 0);
135 if (grub_errno
== GRUB_ERR_NONE
)
137 /* Skip whitespace to find the next digit. */
138 while (*tail
&& grub_isspace (*tail
))
140 grub_env_set (name
, tail
);
144 grub_env_unset (name
);
145 grub_errno
= GRUB_ERR_NONE
;
154 /* Run a menu entry. */
156 grub_menu_execute_entry(grub_menu_entry_t entry
, int auto_boot
)
158 grub_err_t err
= GRUB_ERR_NONE
;
160 grub_menu_t menu
= NULL
;
161 char *optr
, *buf
, *oldchosen
= NULL
, *olddefault
= NULL
;
162 const char *ptr
, *chosen
, *def
;
165 if (entry
->restricted
)
166 err
= grub_auth_check_authentication (entry
->users
);
171 grub_errno
= GRUB_ERR_NONE
;
175 errs_before
= grub_err_printed_errors
;
177 chosen
= grub_env_get ("chosen");
178 def
= grub_env_get ("default");
182 grub_env_context_open ();
183 menu
= grub_zalloc (sizeof (*menu
));
186 grub_env_set_menu (menu
);
188 grub_env_set ("timeout", "0");
191 for (ptr
= entry
->id
; *ptr
; ptr
++)
192 sz
+= (*ptr
== '>') ? 2 : 1;
195 oldchosen
= grub_strdup (chosen
);
201 olddefault
= grub_strdup (def
);
207 sz
+= grub_strlen (chosen
);
209 buf
= grub_malloc (sz
);
217 optr
= grub_stpcpy (optr
, chosen
);
220 for (ptr
= entry
->id
; *ptr
; ptr
++)
227 grub_env_set ("chosen", buf
);
228 grub_env_export ("chosen");
232 for (ptr
= def
; ptr
&& *ptr
; ptr
++)
234 if (ptr
[0] == '>' && ptr
[1] == '>')
243 if (ptr
&& ptr
[0] && ptr
[1])
244 grub_env_set ("default", ptr
+ 1);
246 grub_env_unset ("default");
248 grub_script_execute_sourcecode (entry
->sourcecode
, entry
->argc
, entry
->args
);
250 if (errs_before
!= grub_err_printed_errors
)
251 grub_wait_after_message ();
253 if (grub_errno
== GRUB_ERR_NONE
&& grub_loader_is_loaded ())
254 /* Implicit execution of boot, only if something is loaded. */
255 grub_command_execute ("boot", 0, 0);
259 if (menu
&& menu
->size
)
261 grub_show_menu (menu
, 1, auto_boot
);
262 grub_normal_free_menu (menu
);
264 grub_env_context_close ();
267 grub_env_set ("chosen", oldchosen
);
269 grub_env_unset ("chosen");
271 grub_env_set ("default", olddefault
);
273 grub_env_unset ("default");
274 grub_env_unset ("timeout");
277 /* Execute ENTRY from the menu MENU, falling back to entries specified
278 in the environment variable "fallback" if it fails. CALLBACK is a
279 pointer to a struct of function pointers which are used to allow the
280 caller provide feedback to the user. */
282 grub_menu_execute_with_fallback (grub_menu_t menu
,
283 grub_menu_entry_t entry
,
285 grub_menu_execute_callback_t callback
,
290 callback
->notify_booting (entry
, callback_data
);
292 grub_menu_execute_entry (entry
, 1);
294 /* Deal with fallback entries. */
295 while ((fallback_entry
= get_and_remove_first_entry_number ("fallback"))
299 grub_errno
= GRUB_ERR_NONE
;
301 entry
= grub_menu_get_entry (menu
, fallback_entry
);
302 callback
->notify_fallback (entry
, callback_data
);
303 grub_menu_execute_entry (entry
, 1);
304 /* If the function call to execute the entry returns at all, then this is
305 taken to indicate a boot failure. For menu entries that do something
306 other than actually boot an operating system, this could assume
307 incorrectly that something failed. */
311 callback
->notify_failure (callback_data
);
314 static struct grub_menu_viewer
*viewers
;
317 menu_set_chosen_entry (int entry
)
319 struct grub_menu_viewer
*cur
;
320 for (cur
= viewers
; cur
; cur
= cur
->next
)
321 cur
->set_chosen_entry (entry
, cur
->data
);
325 menu_print_timeout (int timeout
)
327 struct grub_menu_viewer
*cur
;
328 for (cur
= viewers
; cur
; cur
= cur
->next
)
329 cur
->print_timeout (timeout
, cur
->data
);
335 struct grub_menu_viewer
*cur
, *next
;
336 for (cur
= viewers
; cur
; cur
= next
)
339 cur
->fini (cur
->data
);
346 menu_init (int entry
, grub_menu_t menu
, int nested
)
348 struct grub_term_output
*term
;
351 FOR_ACTIVE_TERM_OUTPUTS(term
)
352 if (term
->fullscreen
)
354 if (grub_env_get ("theme"))
356 if (!grub_gfxmenu_try_hook
)
358 grub_dl_load ("gfxmenu");
361 if (grub_gfxmenu_try_hook
)
364 err
= grub_gfxmenu_try_hook (entry
, menu
, nested
);
372 grub_error (GRUB_ERR_BAD_MODULE
,
373 N_("module `%s' isn't loaded"),
376 grub_wait_after_message ();
378 grub_errno
= GRUB_ERR_NONE
;
383 FOR_ACTIVE_TERM_OUTPUTS(term
)
387 if (grub_strcmp (term
->name
, "gfxterm") == 0 && gfxmenu
)
390 err
= grub_menu_try_text (term
, entry
, menu
, nested
);
394 grub_errno
= GRUB_ERR_NONE
;
401 struct grub_menu_viewer
*cur
;
402 for (cur
= viewers
; cur
; cur
= cur
->next
)
403 cur
->clear_timeout (cur
->data
);
407 grub_menu_register_viewer (struct grub_menu_viewer
*viewer
)
409 viewer
->next
= viewers
;
414 menuentry_eq (const char *id
, const char *spec
)
416 const char *ptr1
, *ptr2
;
421 if (*ptr2
== '>' && ptr2
[1] != '>' && *ptr1
== 0)
423 if (*ptr2
== '>' && ptr2
[1] != '>')
437 /* Get the entry number from the variable NAME. */
439 get_entry_number (grub_menu_t menu
, const char *name
)
444 val
= grub_env_get (name
);
450 entry
= (int) grub_strtoul (val
, 0, 0);
452 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
454 /* See if the variable matches the title of a menu entry. */
455 grub_menu_entry_t e
= menu
->entry_list
;
458 grub_errno
= GRUB_ERR_NONE
;
462 if (menuentry_eq (e
->title
, val
)
463 || menuentry_eq (e
->id
, val
))
475 if (grub_errno
!= GRUB_ERR_NONE
)
477 grub_errno
= GRUB_ERR_NONE
;
486 #define GRUB_MENU_PAGE_SIZE 10
488 /* Show the menu and handle menu entry selection. Returns the menu entry
489 index that should be executed or -1 if no entry should be executed (e.g.,
490 Esc pressed to exit a sub-menu or switching menu viewers).
491 If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
492 entry to be executed is a result of an automatic default selection because
495 run_menu (grub_menu_t menu
, int nested
, int *auto_boot
)
497 grub_uint64_t saved_time
;
498 int default_entry
, current_entry
;
501 default_entry
= get_entry_number (menu
, "default");
503 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
505 if (default_entry
< 0 || default_entry
>= menu
->size
)
508 /* If timeout is 0, drawing is pointless (and ugly). */
509 if (grub_menu_get_timeout () == 0)
512 return default_entry
;
515 current_entry
= default_entry
;
517 /* Initialize the time. */
518 saved_time
= grub_get_time_ms ();
521 menu_init (current_entry
, menu
, nested
);
523 timeout
= grub_menu_get_timeout ();
526 menu_print_timeout (timeout
);
533 timeout
= grub_menu_get_timeout ();
535 if (grub_normal_exit_level
)
540 grub_uint64_t current_time
;
542 current_time
= grub_get_time_ms ();
543 if (current_time
- saved_time
>= 1000)
546 grub_menu_set_timeout (timeout
);
547 saved_time
= current_time
;
548 menu_print_timeout (timeout
);
554 grub_env_unset ("timeout");
557 return default_entry
;
560 c
= grub_getkey_noblock ();
562 if (c
!= GRUB_TERM_NO_KEY
)
566 grub_env_unset ("timeout");
567 grub_env_unset ("fallback");
573 case GRUB_TERM_KEY_HOME
:
574 case GRUB_TERM_CTRL
| 'a':
576 menu_set_chosen_entry (current_entry
);
579 case GRUB_TERM_KEY_END
:
580 case GRUB_TERM_CTRL
| 'e':
581 current_entry
= menu
->size
- 1;
582 menu_set_chosen_entry (current_entry
);
585 case GRUB_TERM_KEY_UP
:
586 case GRUB_TERM_CTRL
| 'p':
588 if (current_entry
> 0)
590 menu_set_chosen_entry (current_entry
);
593 case GRUB_TERM_CTRL
| 'n':
594 case GRUB_TERM_KEY_DOWN
:
596 if (current_entry
< menu
->size
- 1)
598 menu_set_chosen_entry (current_entry
);
601 case GRUB_TERM_CTRL
| 'g':
602 case GRUB_TERM_KEY_PPAGE
:
603 if (current_entry
< GRUB_MENU_PAGE_SIZE
)
606 current_entry
-= GRUB_MENU_PAGE_SIZE
;
607 menu_set_chosen_entry (current_entry
);
610 case GRUB_TERM_CTRL
| 'c':
611 case GRUB_TERM_KEY_NPAGE
:
612 if (current_entry
+ GRUB_MENU_PAGE_SIZE
< menu
->size
)
613 current_entry
+= GRUB_MENU_PAGE_SIZE
;
615 current_entry
= menu
->size
- 1;
616 menu_set_chosen_entry (current_entry
);
621 case GRUB_TERM_KEY_RIGHT
:
622 case GRUB_TERM_CTRL
| 'f':
625 return current_entry
;
637 grub_cmdline_run (1);
643 grub_menu_entry_t e
= grub_menu_get_entry (menu
, current_entry
);
645 grub_menu_entry_run (e
);
651 grub_menu_entry_t entry
;
653 for (i
= 0, entry
= menu
->entry_list
; i
< menu
->size
;
654 i
++, entry
= entry
->next
)
655 if (entry
->hotkey
== c
)
667 /* Never reach here. */
670 /* Callback invoked immediately before a menu entry is executed. */
672 notify_booting (grub_menu_entry_t entry
,
673 void *userdata
__attribute__((unused
)))
676 grub_printf_ (N_("Booting `%s'"), entry
->title
);
677 grub_printf ("\n\n");
680 /* Callback invoked when a default menu entry executed because of a timeout
681 has failed and an attempt will be made to execute the next fallback
684 notify_fallback (grub_menu_entry_t entry
,
685 void *userdata
__attribute__((unused
)))
688 grub_printf_ (N_("Falling back to `%s'"), entry
->title
);
689 grub_printf ("\n\n");
690 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS
);
693 /* Callback invoked when a menu entry has failed and there is no remaining
694 fallback entry to attempt. */
696 notify_execution_failure (void *userdata
__attribute__((unused
)))
698 if (grub_errno
!= GRUB_ERR_NONE
)
701 grub_errno
= GRUB_ERR_NONE
;
704 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
705 grub_wait_after_message ();
708 /* Callbacks used by the text menu to provide user feedback when menu entries
710 static struct grub_menu_execute_callback execution_callback
=
712 .notify_booting
= notify_booting
,
713 .notify_fallback
= notify_fallback
,
714 .notify_failure
= notify_execution_failure
718 show_menu (grub_menu_t menu
, int nested
, int autobooted
)
726 boot_entry
= run_menu (menu
, nested
, &auto_boot
);
730 e
= grub_menu_get_entry (menu
, boot_entry
);
732 continue; /* Menu is empty. */
737 grub_menu_execute_with_fallback (menu
, e
, autobooted
,
738 &execution_callback
, 0);
740 grub_menu_execute_entry (e
, 0);
745 return GRUB_ERR_NONE
;
749 grub_show_menu (grub_menu_t menu
, int nested
, int autoboot
)
751 grub_err_t err1
, err2
;
755 err1
= show_menu (menu
, nested
, autoboot
);
759 if (grub_normal_exit_level
)
762 err2
= grub_auth_check_authentication (NULL
);
766 grub_errno
= GRUB_ERR_NONE
;