Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[pxe] Display the "Press F8" prompt rather than displaying menu with …
…timeout

The PXE spec dictates the rather ugly feature that we have to present
a DHCP-specified prompt string to the user, then wait to see if they
press F8 before displaying the menu.

This seems to me to be a significant retrograde step from the current
situation of displaying the menu with the timeout counting down
against the default selected boot option, but apparently the lack of
the "Press F8" prompt causes some confusion.
  • Loading branch information
Michael Brown committed Feb 5, 2009
1 parent 881f1f5 commit 56066a6
Showing 1 changed file with 112 additions and 58 deletions.
170 changes: 112 additions & 58 deletions src/usr/pxemenu.c
Expand Up @@ -26,9 +26,9 @@
#include <curses.h>
#include <console.h>
#include <gpxe/dhcp.h>
#include <gpxe/vsprintf.h>
#include <gpxe/keys.h>
#include <gpxe/timer.h>
#include <gpxe/process.h>
#include <usr/dhcpmgmt.h>
#include <usr/autoboot.h>

Expand Down Expand Up @@ -57,6 +57,8 @@ struct pxe_menu_item {
* options.
*/
struct pxe_menu {
/** Prompt string (optional) */
const char *prompt;
/** Timeout (in seconds)
*
* Negative indicates no timeout (i.e. wait indefinitely)
Expand All @@ -80,22 +82,24 @@ struct pxe_menu {
* boot menu.
*/
static int pxe_menu_parse ( struct pxe_menu **menu ) {
struct setting tmp_setting = { .name = NULL };
struct dhcp_pxe_boot_menu_prompt prompt = { .timeout = 0 };
struct setting pxe_boot_menu_prompt_setting =
{ .tag = DHCP_PXE_BOOT_MENU_PROMPT };
struct setting pxe_boot_menu_setting =
{ .tag = DHCP_PXE_BOOT_MENU };
uint8_t raw_menu[256];
int raw_prompt_len;
int raw_menu_len;
struct dhcp_pxe_boot_menu *raw_menu_item;
struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt;
void *raw_menu_end;
unsigned int num_menu_items;
unsigned int i;
int rc;

/* Fetch relevant settings */
tmp_setting.tag = DHCP_PXE_BOOT_MENU_PROMPT;
fetch_setting ( NULL, &tmp_setting, &prompt, sizeof ( prompt ) );
tmp_setting.tag = DHCP_PXE_BOOT_MENU;
/* Fetch raw menu */
memset ( raw_menu, 0, sizeof ( raw_menu ) );
if ( ( raw_menu_len = fetch_setting ( NULL, &tmp_setting, raw_menu,
if ( ( raw_menu_len = fetch_setting ( NULL, &pxe_boot_menu_setting,
raw_menu,
sizeof ( raw_menu ) ) ) < 0 ) {
rc = raw_menu_len;
DBG ( "Could not retrieve raw PXE boot menu: %s\n",
Expand All @@ -108,6 +112,12 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
}
raw_menu_end = ( raw_menu + raw_menu_len );

/* Fetch raw prompt length */
raw_prompt_len = fetch_setting_len ( NULL,
&pxe_boot_menu_prompt_setting );
if ( raw_prompt_len < 0 )
raw_prompt_len = 0;

/* Count menu items */
num_menu_items = 0;
raw_menu_item = ( ( void * ) raw_menu );
Expand All @@ -128,15 +138,14 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
/* Allocate space for parsed menu */
*menu = zalloc ( sizeof ( **menu ) +
( num_menu_items * sizeof ( (*menu)->items[0] ) ) +
raw_menu_len + 1 /* NUL */ );
raw_menu_len + 1 /* NUL */ +
raw_prompt_len + 1 /* NUL */ );
if ( ! *menu ) {
DBG ( "Could not allocate PXE boot menu\n" );
return -ENOMEM;
}

/* Fill in parsed menu */
(*menu)->timeout =
( ( prompt.timeout == 0xff ) ? -1 : prompt.timeout );
(*menu)->num_items = num_menu_items;
raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
Expand All @@ -153,6 +162,18 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
sizeof ( *raw_menu_item ) +
raw_menu_item->desc_len );
}
if ( raw_prompt_len ) {
raw_menu_prompt = ( ( ( void * ) raw_menu_item ) +
1 /* NUL */ );
fetch_setting ( NULL, &pxe_boot_menu_prompt_setting,
raw_menu_prompt, raw_prompt_len );
(*menu)->timeout =
( ( raw_menu_prompt->timeout == 0xff ) ?
-1 : raw_menu_prompt->timeout );
(*menu)->prompt = raw_menu_prompt->prompt;
} else {
(*menu)->timeout = -1;
}

return 0;
}
Expand All @@ -162,29 +183,20 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
*
* @v menu PXE boot menu
* @v index Index of item to draw
* @v selected Item is selected
*/
static void pxe_menu_draw_item ( struct pxe_menu *menu,
unsigned int index ) {
int selected = ( menu->selection == index );
unsigned int index, int selected ) {
char buf[COLS+1];
char *tmp = buf;
ssize_t remaining = sizeof ( buf );
size_t len;
unsigned int row;

/* Prepare space-padded row content */
len = ssnprintf ( tmp, remaining, " %c. %s",
( 'A' + index ), menu->items[index].desc );
tmp += len;
remaining -= len;
if ( selected && ( menu->timeout > 0 ) ) {
len = ssnprintf ( tmp, remaining, " (%d)", menu->timeout );
tmp += len;
remaining -= len;
}
for ( ; remaining > 1 ; tmp++, remaining-- )
*tmp = ' ';
*tmp = '\0';
len = snprintf ( buf, sizeof ( buf ), " %c. %s",
( 'A' + index ), menu->items[index].desc );
while ( len < ( sizeof ( buf ) - 1 ) )
buf[len++] = ' ';
buf[ sizeof ( buf ) - 1 ] = '\0';

/* Draw row */
row = ( LINES - menu->num_items + index );
Expand All @@ -199,11 +211,7 @@ static void pxe_menu_draw_item ( struct pxe_menu *menu,
* @v menu PXE boot menu
* @ret rc Return status code
*/
int pxe_menu_select ( struct pxe_menu *menu ) {
unsigned long start = currticks();
unsigned long now;
unsigned long elapsed;
unsigned int old_selection;
static int pxe_menu_select ( struct pxe_menu *menu ) {
int key;
unsigned int key_selection;
unsigned int i;
Expand All @@ -220,37 +228,24 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
for ( i = 0 ; i < menu->num_items ; i++ )
printf ( "\n" );
for ( i = 0 ; i < menu->num_items ; i++ )
pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ) );
pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 );

while ( 1 ) {

/* Decrease timeout if necessary */
if ( menu->timeout > 0 ) {
now = currticks();
elapsed = ( now - start );
if ( elapsed >= TICKS_PER_SEC ) {
start = now;
menu->timeout--;
pxe_menu_draw_item ( menu, menu->selection );
}
}

/* Select current item if we have timed out */
if ( menu->timeout == 0 )
break;
/* Highlight currently selected item */
pxe_menu_draw_item ( menu, menu->selection, 1 );

/* Check for keyboard input */
if ( ! iskey() )
continue;
/* Wait for keyboard input */
while ( ! iskey() )
step();
key = getkey();

/* Any keyboard input cancels the timeout */
menu->timeout = -1;
pxe_menu_draw_item ( menu, menu->selection );
/* Unhighlight currently selected item */
pxe_menu_draw_item ( menu, menu->selection, 0 );

/* Act upon key */
old_selection = menu->selection;
if ( ( key == CR ) || ( key == LF ) ) {
pxe_menu_draw_item ( menu, menu->selection, 1 );
break;
} else if ( key == CTRL_C ) {
rc = -ECANCELED;
Expand All @@ -265,10 +260,9 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
( ( key_selection = ( toupper ( key ) - 'A' ) )
< menu->num_items ) ) {
menu->selection = key_selection;
menu->timeout = 0;
pxe_menu_draw_item ( menu, menu->selection, 1 );
break;
}
pxe_menu_draw_item ( menu, old_selection );
pxe_menu_draw_item ( menu, menu->selection );
}

/* Shut down UI */
Expand All @@ -277,6 +271,66 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
return rc;
}

/**
* Prompt for (and make selection from) PXE boot menu
*
* @v menu PXE boot menu
* @ret rc Return status code
*/
static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) {
unsigned long start = currticks();
unsigned long now;
unsigned long elapsed;
size_t len = 0;
int key;
int rc = 0;

/* Display menu immediately, if specified to do so */
if ( menu->timeout < 0 ) {
if ( menu->prompt )
printf ( "%s\n", menu->prompt );
return pxe_menu_select ( menu );
}

/* Display prompt, if specified */
if ( menu->prompt )
printf ( "%s", menu->prompt );

/* Wait for timeout, if specified */
while ( menu->timeout > 0 ) {
if ( ! len )
len = printf ( " (%d)", menu->timeout );
if ( iskey() ) {
key = getkey();
if ( key == KEY_F8 ) {
/* Display menu */
printf ( "\n" );
return pxe_menu_select ( menu );
} else if ( key == CTRL_C ) {
/* Abort */
rc = -ECANCELED;
break;
} else {
/* Stop waiting */
break;
}
}
now = currticks();
elapsed = ( now - start );
if ( elapsed >= TICKS_PER_SEC ) {
menu->timeout -= 1;
do {
printf ( "\b \b" );
} while ( --len );
start = now;
}
}

/* Return with default option selected */
printf ( "\n" );
return rc;
}

/**
* Boot using PXE boot menu
*
Expand All @@ -299,7 +353,7 @@ int pxe_menu_boot ( struct net_device *netdev ) {
return rc;

/* Make selection from boot menu */
if ( ( rc = pxe_menu_select ( menu ) ) != 0 ) {
if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) {
free ( menu );
return rc;
}
Expand Down

0 comments on commit 56066a6

Please sign in to comment.