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 ();
620 key = grub_getkey_noblock ();
621 if (key != GRUB_TERM_NO_KEY)
623 entry = get_entry_index_by_hotkey (menu, key);
627 if (key == GRUB_TERM_ESC)
633 if (timeout > 0 && has_second_elapsed (&saved_time))
636 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
637 print_countdown (pos, timeout);
641 /* We will fall through to auto-booting the default entry. */
645 grub_env_unset ("timeout");
646 grub_env_unset ("timeout_style");
654 /* If timeout is 0, drawing is pointless (and ugly). */
658 return default_entry;
661 current_entry = default_entry;
664 menu_init (current_entry, menu, nested);
666 /* Initialize the time. */
667 saved_time = grub_get_time_ms ();
669 timeout = grub_menu_get_timeout ();
672 menu_print_timeout (timeout);
679 timeout = grub_menu_get_timeout ();
681 if (grub_normal_exit_level)
684 if (timeout > 0 && has_second_elapsed (&saved_time))
687 grub_menu_set_timeout (timeout);
688 menu_print_timeout (timeout);
693 grub_env_unset ("timeout");
696 return default_entry;
699 c = grub_getkey_noblock ();
701 if (c != GRUB_TERM_NO_KEY)
705 grub_env_unset ("timeout");
706 grub_env_unset ("fallback");
712 case GRUB_TERM_KEY_HOME:
713 case GRUB_TERM_CTRL | 'a':
715 menu_set_chosen_entry (current_entry);
718 case GRUB_TERM_KEY_END:
719 case GRUB_TERM_CTRL | 'e':
720 current_entry = menu->size - 1;
721 menu_set_chosen_entry (current_entry);
724 case GRUB_TERM_KEY_UP:
725 case GRUB_TERM_CTRL | 'p':
727 if (current_entry > 0)
729 menu_set_chosen_entry (current_entry);
732 case GRUB_TERM_CTRL | 'n':
733 case GRUB_TERM_KEY_DOWN:
735 if (current_entry < menu->size - 1)
737 menu_set_chosen_entry (current_entry);
740 case GRUB_TERM_CTRL | 'g':
741 case GRUB_TERM_KEY_PPAGE:
742 if (current_entry < GRUB_MENU_PAGE_SIZE)
745 current_entry -= GRUB_MENU_PAGE_SIZE;
746 menu_set_chosen_entry (current_entry);
749 case GRUB_TERM_CTRL | 'c':
750 case GRUB_TERM_KEY_NPAGE:
751 if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
752 current_entry += GRUB_MENU_PAGE_SIZE;
754 current_entry = menu->size - 1;
755 menu_set_chosen_entry (current_entry);
760 case GRUB_TERM_KEY_RIGHT:
761 case GRUB_TERM_CTRL | 'f':
764 return current_entry;
776 grub_cmdline_run (1, 0);
782 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
784 grub_menu_entry_run (e);
792 entry = get_entry_index_by_hotkey (menu, c);
805 /* Never reach here. */
808 /* Callback invoked immediately before a menu entry is executed. */
810 notify_booting (grub_menu_entry_t entry,
811 void *userdata __attribute__((unused)))
814 grub_printf_ (N_("Booting `%s'"), entry->title);
815 grub_printf ("\n\n");
818 /* Callback invoked when a default menu entry executed because of a timeout
819 has failed and an attempt will be made to execute the next fallback
822 notify_fallback (grub_menu_entry_t entry,
823 void *userdata __attribute__((unused)))
826 grub_printf_ (N_("Falling back to `%s'"), entry->title);
827 grub_printf ("\n\n");
828 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
831 /* Callback invoked when a menu entry has failed and there is no remaining
832 fallback entry to attempt. */
834 notify_execution_failure (void *userdata __attribute__((unused)))
836 if (grub_errno != GRUB_ERR_NONE)
839 grub_errno = GRUB_ERR_NONE;
842 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
843 grub_wait_after_message ();
846 /* Callbacks used by the text menu to provide user feedback when menu entries
848 static struct grub_menu_execute_callback execution_callback =
850 .notify_booting = notify_booting,
851 .notify_fallback = notify_fallback,
852 .notify_failure = notify_execution_failure
856 show_menu (grub_menu_t menu, int nested, int autobooted)
864 boot_entry = run_menu (menu, nested, &auto_boot);
868 e = grub_menu_get_entry (menu, boot_entry);
870 continue; /* Menu is empty. */
875 grub_menu_execute_with_fallback (menu, e, autobooted,
876 &execution_callback, 0);
878 grub_menu_execute_entry (e, 0);
883 return GRUB_ERR_NONE;
887 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
889 grub_err_t err1, err2;
893 err1 = show_menu (menu, nested, autoboot);
897 if (grub_normal_exit_level)
900 err2 = grub_auth_check_authentication (NULL);
904 grub_errno = GRUB_ERR_NONE;