stage2.c revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2004,2005 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <shared.h>
#include <term.h>
int reset_term;
#if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS)
# if defined(PRESET_MENU_STRING)
static const char *preset_menu = PRESET_MENU_STRING;
# elif defined(SUPPORT_DISKLESS)
/* Execute the command "bootp" automatically. */
static const char *preset_menu = "dhcp\n";
# endif /* SUPPORT_DISKLESS */
static int preset_menu_offset;
static int
open_preset_menu (void)
{
#ifdef GRUB_UTIL
/* Unless the user explicitly requests to use the preset menu,
always opening the preset menu fails in the grub shell. */
if (! use_preset_menu)
return 0;
#endif /* GRUB_UTIL */
preset_menu_offset = 0;
return preset_menu != 0;
}
static int
{
return len;
}
static void
close_preset_menu (void)
{
/* Disable the preset menu. */
preset_menu = 0;
}
#else /* ! PRESET_MENU_STRING && ! SUPPORT_DISKLESS */
#define open_preset_menu() 0
#define close_preset_menu()
#endif /* ! PRESET_MENU_STRING && ! SUPPORT_DISKLESS */
static char *
{
int i;
for (i = 0; i < num; i++)
{
do
{
while (*(list++));
}
}
return list;
}
/* Print an entry in a line of the menu box. */
static void
{
int x;
if (current_term->setcolorstate)
gotoxy (2, y);
grub_putchar (' ');
for (x = 3; x < 75; x++)
{
if (*entry && x <= 72)
{
if (x == 72)
else
grub_putchar (*entry++);
}
else
grub_putchar (' ');
}
gotoxy (74, y);
if (current_term->setcolorstate)
}
/* Print entries in the menu box. */
static void
{
int i;
if (first)
else
grub_putchar (' ');
for (i = 0; i < size; i++)
{
while (*menu_entries)
menu_entries++;
if (*(menu_entries - 1))
menu_entries++;
}
if (*menu_entries)
else
grub_putchar (' ');
}
static void
{
int i;
#define LINE_LENGTH 67
for (i = 0; i < LINE_LENGTH; i++)
grub_putchar ('-');
grub_putchar ('\n');
{
/* grub's printf can't %02d so ... */
if (i < 10)
grub_putchar (' ');
}
for (i = 0; i < LINE_LENGTH; i++)
grub_putchar ('-');
grub_putchar ('\n');
}
static void
print_border (int y, int size)
{
int i;
if (current_term->setcolorstate)
gotoxy (1, y);
for (i = 0; i < 73; i++)
i = 1;
while (1)
{
gotoxy (1, y + i);
if (i > size)
break;
gotoxy (75, y + i);
i++;
}
for (i = 0; i < 73; i++)
if (current_term->setcolorstate)
}
static void
{
char *cur_entry = 0;
/*
* Main loop for menu UI.
*/
/* Dumb terminal always use all entries for display
invariant for TERM_DUMB: first_entry == 0 */
{
while (entryno > 11)
{
first_entry++;
entryno--;
}
}
/* If the timeout was expired or wasn't set, force to show the menu
interface. */
if (grub_timeout < 0)
show_menu = 1;
/* If SHOW_MENU is false, don't display the menu until ESC is pressed. */
if (! show_menu)
{
/* Get current time. */
;
while (1)
{
/* Check if ESC is pressed. */
{
grub_timeout = -1;
show_menu = 1;
break;
}
/* If GRUB_TIMEOUT is expired, boot the default entry. */
if (grub_timeout >=0
&& time1 != 0xFF)
{
if (grub_timeout <= 0)
{
grub_timeout = -1;
goto boot_entry;
}
grub_timeout--;
/* Print a message. */
grub_printf ("\rPress `ESC' to enter the menu... %d ",
}
}
}
/* Only display the menu if the user wants to see it. */
if (show_menu)
{
init_page ();
setcursor (0);
else
grub_printf ("\n\
Use the %c and %c keys to select which entry is highlighted.\n",
{
printf ("\
Press enter to boot the selected OS or \'p\' to enter a\n\
password to unlock the next set of features.");
}
else
{
if (config_entries)
printf ("\
Press enter to boot the selected OS, \'e\' to edit the\n\
commands before booting, or \'c\' for a command-line.");
else
printf ("\
Press \'b\' to boot, \'e\' to edit the selected command in the\n\
boot sequence, \'c\' for a command-line, \'o\' to open a new line\n\
after (\'O\' for before) the selected line, \'d\' to remove the\n\
selected line, or escape to go back to the main menu.");
}
else
}
/* XX using RT clock now, need to initialize value */
while (1)
{
/* Initialize to NULL just in case... */
{
if (grub_timeout <= 0)
{
grub_timeout = -1;
break;
}
/* else not booting yet! */
grub_printf ("\r Entry %d will be booted automatically in %d seconds. ",
else
{
grub_printf ("The highlighted entry will be booted automatically in %d seconds. ",
}
grub_timeout--;
}
/* Check for a keypress, however if TIMEOUT has been expired
(GRUB_TIMEOUT == -1) relax in GETKEY even if no key has been
pressed.
This avoids polling (relevant in the grub-shell and later on
in grub if interrupt driven I/O is done). */
if (checkkey () >= 0 || grub_timeout < 0)
{
/* Key was pressed, show which entry is selected before GETKEY,
since we're comming in here also on GRUB_TIMEOUT == -1 and
hang in GETKEY */
c = ASCII_CHAR (getkey ());
if (grub_timeout >= 0)
{
grub_putchar ('\r');
else
printf (" ");
grub_timeout = -1;
fallback_entryno = -1;
}
/* We told them above (at least in SUPPORT_SERIAL) to use
'^' or 'v' so accept these keys. */
if (c == 16 || c == '^')
{
{
if (entryno > 0)
entryno--;
}
else
{
if (entryno > 0)
{
0));
entryno--;
0));
}
else if (first_entry > 0)
{
first_entry--;
}
}
}
else if ((c == 14 || c == 'v')
{
entryno++;
else
{
if (entryno < 11)
{
0));
entryno++;
0));
}
{
first_entry++;
}
}
}
else if (c == 7)
{
/* Page Up */
first_entry -= 12;
if (first_entry < 0)
{
entryno += first_entry;
first_entry = 0;
if (entryno < 0)
entryno = 0;
}
}
else if (c == 3)
{
/* Page Down */
first_entry += 12;
{
if (first_entry < 0)
first_entry = 0;
}
}
if (config_entries)
{
if ((c == '\n') || (c == '\r') || (c == 6))
break;
}
else
{
if ((c == 'd') || (c == 'o') || (c == 'O'))
{
0));
/* insert after is almost exactly like insert before */
if (c == 'o')
{
/* But `o' differs from `O', since it may causes
the menu screen to scroll up. */
entryno++;
else
first_entry++;
c = 'O';
}
0);
if (c == 'O')
{
cur_entry[0] = ' ';
cur_entry[1] = 0;
heap += 2;
num_entries++;
}
else if (num_entries > 0)
{
0);
num_entries--;
if (entryno >= num_entries)
entryno--;
first_entry--;
}
{
grub_printf ("\n\n");
grub_printf ("\n");
}
else
}
if (c == 27)
return;
if (c == 'b')
break;
}
{
if (c == 'p')
{
/* Do password check here! */
char entered[32];
grub_printf ("\r ");
else
/* Wipe out the previously entered password */
pptr++;
/* Make sure that PASSWORD is NUL-terminated. */
*pptr++ = 0;
{
char *new_file = config_file;
pptr++;
/* If *PPTR is NUL, then allow the user to use
privileged instructions, otherwise, load
another configuration file. */
if (*pptr != 0)
{
;
/* Make sure that the user will not have
authority in the next configuration. */
auth = 0;
return;
}
else
{
/* Now the user is superhuman. */
auth = 1;
goto restart;
}
}
else
{
grub_printf ("Failed!\n Press any key to continue...");
getkey ();
goto restart;
}
}
}
else
{
if (c == 'e')
{
int new_num_entries = 0, i = 0;
char *new_heap;
if (config_entries)
{
1);
}
else
{
/* safe area! */
0);
}
do
{
}
while (config_entries && cur_entry[i]);
/* this only needs to be done if config_entries is non-NULL,
but it doesn't hurt to do it always */
*(new_heap++) = 0;
if (config_entries)
else
{
cls ();
{
int j = 0;
/* get length of new command */
while (new_heap[j++])
;
if (j < 2)
{
j = 2;
new_heap[0] = ' ';
new_heap[1] = 0;
}
/* align rest of commands properly */
/* copy command to correct area */
heap += (j - i);
}
}
goto restart;
}
if (c == 'c')
{
enter_cmdline (heap, 0);
goto restart;
}
#ifdef GRUB_UTIL
if (c == 'q')
{
/* The same as ``quit''. */
stop ();
}
#endif
}
}
}
/* Attempt to boot an entry. */
reset_term = 1;
cls ();
setcursor (1);
/* if our terminal needed initialization, we should shut it down
* before booting the kernel, but we want to save what it was so
* we can come back if needed */
if (current_term->shutdown) {
(*current_term->shutdown)();
}
while (1)
{
if (config_entries)
printf (" Booting \'%s\'\n\n",
else
printf (" Booting command-list\n\n");
if (! cur_entry)
/* Set CURRENT_ENTRYNO for the command "savedefault". */
{
if (fallback_entryno >= 0)
{
first_entry = 0;
|| fallback_entries[fallback_entryno] < 0)
fallback_entryno = -1;
}
else
break;
}
else
break;
}
/* if we get back here, we should go back to what our term was before */
if (current_term->startup)
/* if our terminal fails to initialize, fall back to console since
* it should always work */
if ((*current_term->startup)() == 0)
}
show_menu = 1;
goto restart;
}
static int
{
char c; /* since we're loading it a byte at a time! */
while (1)
{
if (read_from_file)
{
if (! grub_read (&c, 1))
break;
}
else
{
if (! read_from_preset_menu (&c, 1))
break;
}
/* Skip all carriage returns. */
if (c == '\r')
continue;
/* Replace tabs with spaces. */
if (c == '\t')
c = ' ';
/* The previous is a backslash, then... */
if (literal)
{
/* If it is a newline, replace it with a space and continue. */
if (c == '\n')
{
c = ' ';
/* Go back to overwrite a backslash. */
if (pos > 0)
pos--;
}
literal = 0;
}
/* translate characters first! */
if (c == '\\' && ! literal)
literal = 1;
if (comment)
{
if (c == '\n')
comment = 0;
}
else if (! pos)
{
if (c == '#')
comment = 1;
else if ((c != ' ') && (c != '\n'))
}
else
{
if (c == '\n')
break;
}
}
return pos;
}
/* This is the starting function in C. */
void
cmain (void)
{
char *config_entries, *menu_entries;
auto void reset (void);
void reset (void)
{
count_lines = -1;
config_len = 0;
menu_len = 0;
num_entries = 0;
menu_entries = (char *) MENU_BUF;
init_config ();
}
/* Initialize the environment for restarting Stage 2. */
/* Initialize the kill buffer. */
*kill_buf = 0;
/* Never return. */
for (;;)
{
reset ();
/* Here load the configuration file. */
#ifdef GRUB_UTIL
if (use_config_file)
#endif /* GRUB_UTIL */
{
char *default_file = (char *) DEFAULT_FILE_BUF;
int i;
/* Get a saved default entry if possible. */
saved_entryno = 0;
for (i = grub_strlen(default_file); i >= 0; i--)
if (default_file[i] == '/')
{
i++;
break;
}
default_file[i] = 0;
if (grub_open (default_file))
{
char *p = buf;
int len;
if (len > 0)
{
safe_parse_maxint (&p, &saved_entryno);
}
grub_close ();
}
do
{
/* STATE 0: Before any title command.
STATE 1: In a title command.
STATE >1: In a entry after a title command. */
char *cmdline;
/* Try the preset menu first. This will succeed at most once,
because close_preset_menu disables the preset menu. */
if (! is_opened)
{
}
if (! is_opened)
break;
/* This is necessary, because the menu must be overrided. */
reset ();
cmdline = (char *) CMDLINE_BUF;
! is_preset))
{
/* Get the pointer to the builtin structure. */
errnum = 0;
if (! builtin)
/* Unknown command. Just skip now. */
continue;
{
char *ptr;
/* the command "title" is specially treated. */
if (state > 1)
{
/* The next title is found. */
num_entries++;
config_entries[config_len++] = 0;
}
else
{
/* The first title is found. */
}
/* Reset the state. */
state = 1;
/* Copy title into menu area. */
;
}
else if (! state)
{
/* Run a command found is possible. */
{
errnum = 0;
}
else
/* Ignored. */
continue;
}
else
{
state++;
/* Copy config file data to config area. */
;
}
}
if (state > 1)
{
/* Finish the last entry. */
num_entries++;
config_entries[config_len++] = 0;
}
else
{
}
menu_entries[menu_len++] = 0;
config_entries[config_len++] = 0;
menu_len);
/* Make sure that all fallback entries are valid. */
if (fallback_entryno >= 0)
{
for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
{
if (fallback_entries[i] < 0)
break;
if (fallback_entries[i] >= num_entries)
{
grub_memmove (fallback_entries + i,
fallback_entries + i + 1,
((MAX_FALLBACK_ENTRIES - i - 1)
* sizeof (int)));
i--;
}
}
if (fallback_entries[0] < 0)
fallback_entryno = -1;
}
/* Check if the default entry is present. Otherwise reset
it to fallback if fallback is valid, or to DEFAULT_ENTRY
if not. */
if (default_entry >= num_entries)
{
if (fallback_entryno >= 0)
{
|| fallback_entries[fallback_entryno] < 0)
fallback_entryno = -1;
}
else
default_entry = 0;
}
if (is_preset)
else
grub_close ();
}
while (is_preset);
}
/* go ahead and make sure the terminal is setup */
if (current_term->startup)
(*current_term->startup)();
if (! num_entries)
{
/* If no acceptable config file, goto command-line, starting
heap from where the config entries would have been stored
if there were any. */
}
else
{
/* Run menu interface. */
}
}
}