Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / normal / menu.c
blob7e0a15859073cb0a703bb9a5e795b6cfef09c282
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.
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>
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>
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,
41 int nested) = NULL;
43 /* Wait until the user pushes any key so that the user
44 can see what happened. */
45 void
46 grub_wait_after_message (void)
48 grub_uint64_t endtime;
49 grub_xputs ("\n");
50 grub_printf_ (N_("Press any key to continue..."));
51 grub_refresh ();
53 endtime = grub_get_time_ms () + 10000;
55 while (grub_get_time_ms () < endtime
56 && grub_getkey_noblock () == GRUB_TERM_NO_KEY);
58 grub_xputs ("\n");
61 /* Get a menu entry by its index in the entry list. */
62 grub_menu_entry_t
63 grub_menu_get_entry (grub_menu_t menu, int no)
65 grub_menu_entry_t e;
67 for (e = menu->entry_list; e && no > 0; e = e->next, no--)
70 return e;
73 /* Return the current timeout. If the variable "timeout" is not set or
74 invalid, return -1. */
75 int
76 grub_menu_get_timeout (void)
78 const char *val;
79 int timeout;
81 val = grub_env_get ("timeout");
82 if (! val)
83 return -1;
85 grub_error_push ();
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;
94 timeout = -1;
97 grub_error_pop ();
99 return timeout;
102 /* Set current timeout in the variable "timeout". */
103 void
104 grub_menu_set_timeout (int timeout)
106 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
107 if (timeout > 0)
109 char buf[16];
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. */
120 static int
121 get_and_remove_first_entry_number (const char *name)
123 const char *val;
124 char *tail;
125 int entry;
127 val = grub_env_get (name);
128 if (! val)
129 return -1;
131 grub_error_push ();
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))
139 tail++;
140 grub_env_set (name, tail);
142 else
144 grub_env_unset (name);
145 grub_errno = GRUB_ERR_NONE;
146 entry = -1;
149 grub_error_pop ();
151 return entry;
154 /* Run a menu entry. */
155 static void
156 grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
158 grub_err_t err = GRUB_ERR_NONE;
159 int errs_before;
160 grub_menu_t menu = NULL;
161 char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
162 const char *ptr, *chosen, *def;
163 grub_size_t sz = 0;
165 if (entry->restricted)
166 err = grub_auth_check_authentication (entry->users);
168 if (err)
170 grub_print_error ();
171 grub_errno = GRUB_ERR_NONE;
172 return;
175 errs_before = grub_err_printed_errors;
177 chosen = grub_env_get ("chosen");
178 def = grub_env_get ("default");
180 if (entry->submenu)
182 grub_env_context_open ();
183 menu = grub_zalloc (sizeof (*menu));
184 if (! menu)
185 return;
186 grub_env_set_menu (menu);
187 if (auto_boot)
188 grub_env_set ("timeout", "0");
191 for (ptr = entry->id; *ptr; ptr++)
192 sz += (*ptr == '>') ? 2 : 1;
193 if (chosen)
195 oldchosen = grub_strdup (chosen);
196 if (!oldchosen)
197 grub_print_error ();
199 if (def)
201 olddefault = grub_strdup (def);
202 if (!olddefault)
203 grub_print_error ();
205 sz++;
206 if (chosen)
207 sz += grub_strlen (chosen);
208 sz++;
209 buf = grub_malloc (sz);
210 if (!buf)
211 grub_print_error ();
212 else
214 optr = buf;
215 if (chosen)
217 optr = grub_stpcpy (optr, chosen);
218 *optr++ = '>';
220 for (ptr = entry->id; *ptr; ptr++)
222 if (*ptr == '>')
223 *optr++ = '>';
224 *optr++ = *ptr;
226 *optr = 0;
227 grub_env_set ("chosen", buf);
228 grub_env_export ("chosen");
229 grub_free (buf);
232 for (ptr = def; ptr && *ptr; ptr++)
234 if (ptr[0] == '>' && ptr[1] == '>')
236 ptr++;
237 continue;
239 if (ptr[0] == '>')
240 break;
243 if (ptr && ptr[0] && ptr[1])
244 grub_env_set ("default", ptr + 1);
245 else
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);
257 if (entry->submenu)
259 if (menu && menu->size)
261 grub_show_menu (menu, 1, auto_boot);
262 grub_normal_free_menu (menu);
264 grub_env_context_close ();
266 if (oldchosen)
267 grub_env_set ("chosen", oldchosen);
268 else
269 grub_env_unset ("chosen");
270 if (olddefault)
271 grub_env_set ("default", olddefault);
272 else
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. */
281 static void
282 grub_menu_execute_with_fallback (grub_menu_t menu,
283 grub_menu_entry_t entry,
284 int autobooted,
285 grub_menu_execute_callback_t callback,
286 void *callback_data)
288 int fallback_entry;
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"))
296 >= 0)
298 grub_print_error ();
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. */
310 if (!autobooted)
311 callback->notify_failure (callback_data);
314 static struct grub_menu_viewer *viewers;
316 static void
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);
324 static void
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);
332 static void
333 menu_fini (void)
335 struct grub_menu_viewer *cur, *next;
336 for (cur = viewers; cur; cur = next)
338 next = cur->next;
339 cur->fini (cur->data);
340 grub_free (cur);
342 viewers = NULL;
345 static void
346 menu_init (int entry, grub_menu_t menu, int nested)
348 struct grub_term_output *term;
349 int gfxmenu = 0;
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");
359 grub_print_error ();
361 if (grub_gfxmenu_try_hook)
363 grub_err_t err;
364 err = grub_gfxmenu_try_hook (entry, menu, nested);
365 if(!err)
367 gfxmenu = 1;
368 break;
371 else
372 grub_error (GRUB_ERR_BAD_MODULE,
373 N_("module `%s' isn't loaded"),
374 "gfxmenu");
375 grub_print_error ();
376 grub_wait_after_message ();
378 grub_errno = GRUB_ERR_NONE;
379 term->fullscreen ();
380 break;
383 FOR_ACTIVE_TERM_OUTPUTS(term)
385 grub_err_t err;
387 if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
388 continue;
390 err = grub_menu_try_text (term, entry, menu, nested);
391 if(!err)
392 continue;
393 grub_print_error ();
394 grub_errno = GRUB_ERR_NONE;
398 static void
399 clear_timeout (void)
401 struct grub_menu_viewer *cur;
402 for (cur = viewers; cur; cur = cur->next)
403 cur->clear_timeout (cur->data);
406 void
407 grub_menu_register_viewer (struct grub_menu_viewer *viewer)
409 viewer->next = viewers;
410 viewers = viewer;
413 static int
414 menuentry_eq (const char *id, const char *spec)
416 const char *ptr1, *ptr2;
417 ptr1 = id;
418 ptr2 = spec;
419 while (1)
421 if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
422 return 1;
423 if (*ptr2 == '>' && ptr2[1] != '>')
424 return 0;
425 if (*ptr2 == '>')
426 ptr2++;
427 if (*ptr1 != *ptr2)
428 return 0;
429 if (*ptr1 == 0)
430 return 1;
431 ptr1++;
432 ptr2++;
437 /* Get the entry number from the variable NAME. */
438 static int
439 get_entry_number (grub_menu_t menu, const char *name)
441 const char *val;
442 int entry;
444 val = grub_env_get (name);
445 if (! val)
446 return -1;
448 grub_error_push ();
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;
456 int i;
458 grub_errno = GRUB_ERR_NONE;
460 for (i = 0; e; i++)
462 if (menuentry_eq (e->title, val)
463 || menuentry_eq (e->id, val))
465 entry = i;
466 break;
468 e = e->next;
471 if (! e)
472 entry = -1;
475 if (grub_errno != GRUB_ERR_NONE)
477 grub_errno = GRUB_ERR_NONE;
478 entry = -1;
481 grub_error_pop ();
483 return entry;
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
493 of the timeout. */
494 static int
495 run_menu (grub_menu_t menu, int nested, int *auto_boot)
497 grub_uint64_t saved_time;
498 int default_entry, current_entry;
499 int timeout;
501 default_entry = get_entry_number (menu, "default");
503 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
504 the first entry. */
505 if (default_entry < 0 || default_entry >= menu->size)
506 default_entry = 0;
508 /* If timeout is 0, drawing is pointless (and ugly). */
509 if (grub_menu_get_timeout () == 0)
511 *auto_boot = 1;
512 return default_entry;
515 current_entry = default_entry;
517 /* Initialize the time. */
518 saved_time = grub_get_time_ms ();
520 refresh:
521 menu_init (current_entry, menu, nested);
523 timeout = grub_menu_get_timeout ();
525 if (timeout > 0)
526 menu_print_timeout (timeout);
527 else
528 clear_timeout ();
530 while (1)
532 int c;
533 timeout = grub_menu_get_timeout ();
535 if (grub_normal_exit_level)
536 return -1;
538 if (timeout > 0)
540 grub_uint64_t current_time;
542 current_time = grub_get_time_ms ();
543 if (current_time - saved_time >= 1000)
545 timeout--;
546 grub_menu_set_timeout (timeout);
547 saved_time = current_time;
548 menu_print_timeout (timeout);
552 if (timeout == 0)
554 grub_env_unset ("timeout");
555 *auto_boot = 1;
556 menu_fini ();
557 return default_entry;
560 c = grub_getkey_noblock ();
562 if (c != GRUB_TERM_NO_KEY)
564 if (timeout >= 0)
566 grub_env_unset ("timeout");
567 grub_env_unset ("fallback");
568 clear_timeout ();
571 switch (c)
573 case GRUB_TERM_KEY_HOME:
574 case GRUB_TERM_CTRL | 'a':
575 current_entry = 0;
576 menu_set_chosen_entry (current_entry);
577 break;
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);
583 break;
585 case GRUB_TERM_KEY_UP:
586 case GRUB_TERM_CTRL | 'p':
587 case '^':
588 if (current_entry > 0)
589 current_entry--;
590 menu_set_chosen_entry (current_entry);
591 break;
593 case GRUB_TERM_CTRL | 'n':
594 case GRUB_TERM_KEY_DOWN:
595 case 'v':
596 if (current_entry < menu->size - 1)
597 current_entry++;
598 menu_set_chosen_entry (current_entry);
599 break;
601 case GRUB_TERM_CTRL | 'g':
602 case GRUB_TERM_KEY_PPAGE:
603 if (current_entry < GRUB_MENU_PAGE_SIZE)
604 current_entry = 0;
605 else
606 current_entry -= GRUB_MENU_PAGE_SIZE;
607 menu_set_chosen_entry (current_entry);
608 break;
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;
614 else
615 current_entry = menu->size - 1;
616 menu_set_chosen_entry (current_entry);
617 break;
619 case '\n':
620 case '\r':
621 case GRUB_TERM_KEY_RIGHT:
622 case GRUB_TERM_CTRL | 'f':
623 menu_fini ();
624 *auto_boot = 0;
625 return current_entry;
627 case '\e':
628 if (nested)
630 menu_fini ();
631 return -1;
633 break;
635 case 'c':
636 menu_fini ();
637 grub_cmdline_run (1);
638 goto refresh;
640 case 'e':
641 menu_fini ();
643 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
644 if (e)
645 grub_menu_entry_run (e);
647 goto refresh;
649 default:
651 grub_menu_entry_t entry;
652 int i;
653 for (i = 0, entry = menu->entry_list; i < menu->size;
654 i++, entry = entry->next)
655 if (entry->hotkey == c)
657 menu_fini ();
658 *auto_boot = 0;
659 return i;
662 break;
667 /* Never reach here. */
670 /* Callback invoked immediately before a menu entry is executed. */
671 static void
672 notify_booting (grub_menu_entry_t entry,
673 void *userdata __attribute__((unused)))
675 grub_printf (" ");
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
682 entry, ENTRY. */
683 static void
684 notify_fallback (grub_menu_entry_t entry,
685 void *userdata __attribute__((unused)))
687 grub_printf ("\n ");
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. */
695 static void
696 notify_execution_failure (void *userdata __attribute__((unused)))
698 if (grub_errno != GRUB_ERR_NONE)
700 grub_print_error ();
701 grub_errno = GRUB_ERR_NONE;
703 grub_printf ("\n ");
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
709 are executed. */
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
717 static grub_err_t
718 show_menu (grub_menu_t menu, int nested, int autobooted)
720 while (1)
722 int boot_entry;
723 grub_menu_entry_t e;
724 int auto_boot;
726 boot_entry = run_menu (menu, nested, &auto_boot);
727 if (boot_entry < 0)
728 break;
730 e = grub_menu_get_entry (menu, boot_entry);
731 if (! e)
732 continue; /* Menu is empty. */
734 grub_cls ();
736 if (auto_boot)
737 grub_menu_execute_with_fallback (menu, e, autobooted,
738 &execution_callback, 0);
739 else
740 grub_menu_execute_entry (e, 0);
741 if (autobooted)
742 break;
745 return GRUB_ERR_NONE;
748 grub_err_t
749 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
751 grub_err_t err1, err2;
753 while (1)
755 err1 = show_menu (menu, nested, autoboot);
756 autoboot = 0;
757 grub_print_error ();
759 if (grub_normal_exit_level)
760 break;
762 err2 = grub_auth_check_authentication (NULL);
763 if (err2)
765 grub_print_error ();
766 grub_errno = GRUB_ERR_NONE;
767 continue;
770 break;
773 return err1;