summaryrefslogtreecommitdiffstats
path: root/src/usr/pxemenu.c
diff options
context:
space:
mode:
authorMichael Brown2009-02-05 20:29:16 +0100
committerMichael Brown2009-02-05 20:29:16 +0100
commit56066a6085f936da4495fe7b163fa1e58a2a0b6d (patch)
tree9c13648a47be10cc961b1de26ea487e60702f45f /src/usr/pxemenu.c
parent[pxe] Obey lists of PXE Boot Servers and associated Discovery Control bits (diff)
downloadipxe-56066a6085f936da4495fe7b163fa1e58a2a0b6d.tar.gz
ipxe-56066a6085f936da4495fe7b163fa1e58a2a0b6d.tar.xz
ipxe-56066a6085f936da4495fe7b163fa1e58a2a0b6d.zip
[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.
Diffstat (limited to 'src/usr/pxemenu.c')
-rw-r--r--src/usr/pxemenu.c170
1 files changed, 112 insertions, 58 deletions
diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c
index 4cc40a65..911d4e9a 100644
--- a/src/usr/pxemenu.c
+++ b/src/usr/pxemenu.c
@@ -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>
@@ -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)
@@ -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",
@@ -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 );
@@ -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] ) ) );
@@ -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;
}
@@ -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 );
@@ -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;
@@ -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;
@@ -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 */
@@ -278,6 +272,66 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
}
/**
+ * 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
*
* @ret rc Return status code
@@ -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;
}