Browse Source

[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.
tags/v0.9.7
Michael Brown 16 years ago
parent
commit
56066a6085
1 changed files with 112 additions and 58 deletions
  1. 112
    58
      src/usr/pxemenu.c

+ 112
- 58
src/usr/pxemenu.c View File

26
 #include <curses.h>
26
 #include <curses.h>
27
 #include <console.h>
27
 #include <console.h>
28
 #include <gpxe/dhcp.h>
28
 #include <gpxe/dhcp.h>
29
-#include <gpxe/vsprintf.h>
30
 #include <gpxe/keys.h>
29
 #include <gpxe/keys.h>
31
 #include <gpxe/timer.h>
30
 #include <gpxe/timer.h>
31
+#include <gpxe/process.h>
32
 #include <usr/dhcpmgmt.h>
32
 #include <usr/dhcpmgmt.h>
33
 #include <usr/autoboot.h>
33
 #include <usr/autoboot.h>
34
 
34
 
57
  * options.
57
  * options.
58
  */
58
  */
59
 struct pxe_menu {
59
 struct pxe_menu {
60
+	/** Prompt string (optional) */
61
+	const char *prompt;
60
 	/** Timeout (in seconds)
62
 	/** Timeout (in seconds)
61
 	 *
63
 	 *
62
 	 * Negative indicates no timeout (i.e. wait indefinitely)
64
 	 * Negative indicates no timeout (i.e. wait indefinitely)
80
  * boot menu.
82
  * boot menu.
81
  */
83
  */
82
 static int pxe_menu_parse ( struct pxe_menu **menu ) {
84
 static int pxe_menu_parse ( struct pxe_menu **menu ) {
83
-	struct setting tmp_setting = { .name = NULL };
84
-	struct dhcp_pxe_boot_menu_prompt prompt = { .timeout = 0 };
85
+	struct setting pxe_boot_menu_prompt_setting =
86
+		{ .tag = DHCP_PXE_BOOT_MENU_PROMPT };
87
+	struct setting pxe_boot_menu_setting =
88
+		{ .tag = DHCP_PXE_BOOT_MENU };
85
 	uint8_t raw_menu[256];
89
 	uint8_t raw_menu[256];
90
+	int raw_prompt_len;
86
 	int raw_menu_len;
91
 	int raw_menu_len;
87
 	struct dhcp_pxe_boot_menu *raw_menu_item;
92
 	struct dhcp_pxe_boot_menu *raw_menu_item;
93
+	struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt;
88
 	void *raw_menu_end;
94
 	void *raw_menu_end;
89
 	unsigned int num_menu_items;
95
 	unsigned int num_menu_items;
90
 	unsigned int i;
96
 	unsigned int i;
91
 	int rc;
97
 	int rc;
92
 
98
 
93
-	/* Fetch relevant settings */
94
-	tmp_setting.tag = DHCP_PXE_BOOT_MENU_PROMPT;
95
-	fetch_setting ( NULL, &tmp_setting, &prompt, sizeof ( prompt ) );
96
-	tmp_setting.tag = DHCP_PXE_BOOT_MENU;
99
+	/* Fetch raw menu */
97
 	memset ( raw_menu, 0, sizeof ( raw_menu ) );
100
 	memset ( raw_menu, 0, sizeof ( raw_menu ) );
98
-	if ( ( raw_menu_len = fetch_setting ( NULL, &tmp_setting, raw_menu,
101
+	if ( ( raw_menu_len = fetch_setting ( NULL, &pxe_boot_menu_setting,
102
+					      raw_menu,
99
 					      sizeof ( raw_menu ) ) ) < 0 ) {
103
 					      sizeof ( raw_menu ) ) ) < 0 ) {
100
 		rc = raw_menu_len;
104
 		rc = raw_menu_len;
101
 		DBG ( "Could not retrieve raw PXE boot menu: %s\n",
105
 		DBG ( "Could not retrieve raw PXE boot menu: %s\n",
108
 	}
112
 	}
109
 	raw_menu_end = ( raw_menu + raw_menu_len );
113
 	raw_menu_end = ( raw_menu + raw_menu_len );
110
 
114
 
115
+	/* Fetch raw prompt length */
116
+	raw_prompt_len = fetch_setting_len ( NULL,
117
+					     &pxe_boot_menu_prompt_setting );
118
+	if ( raw_prompt_len < 0 )
119
+		raw_prompt_len = 0;
120
+
111
 	/* Count menu items */
121
 	/* Count menu items */
112
 	num_menu_items = 0;
122
 	num_menu_items = 0;
113
 	raw_menu_item = ( ( void * ) raw_menu );
123
 	raw_menu_item = ( ( void * ) raw_menu );
128
 	/* Allocate space for parsed menu */
138
 	/* Allocate space for parsed menu */
129
 	*menu = zalloc ( sizeof ( **menu ) +
139
 	*menu = zalloc ( sizeof ( **menu ) +
130
 			 ( num_menu_items * sizeof ( (*menu)->items[0] ) ) +
140
 			 ( num_menu_items * sizeof ( (*menu)->items[0] ) ) +
131
-			 raw_menu_len + 1 /* NUL */ );
141
+			 raw_menu_len + 1 /* NUL */ +
142
+			 raw_prompt_len + 1 /* NUL */ );
132
 	if ( ! *menu ) {
143
 	if ( ! *menu ) {
133
 		DBG ( "Could not allocate PXE boot menu\n" );
144
 		DBG ( "Could not allocate PXE boot menu\n" );
134
 		return -ENOMEM;
145
 		return -ENOMEM;
135
 	}
146
 	}
136
 
147
 
137
 	/* Fill in parsed menu */
148
 	/* Fill in parsed menu */
138
-	(*menu)->timeout =
139
-		( ( prompt.timeout == 0xff ) ? -1 : prompt.timeout );
140
 	(*menu)->num_items = num_menu_items;
149
 	(*menu)->num_items = num_menu_items;
141
 	raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
150
 	raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
142
 			  ( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
151
 			  ( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
153
 				  sizeof ( *raw_menu_item ) +
162
 				  sizeof ( *raw_menu_item ) +
154
 				  raw_menu_item->desc_len );
163
 				  raw_menu_item->desc_len );
155
 	}
164
 	}
165
+	if ( raw_prompt_len ) {
166
+		raw_menu_prompt = ( ( ( void * ) raw_menu_item ) +
167
+				    1 /* NUL */ );
168
+		fetch_setting ( NULL, &pxe_boot_menu_prompt_setting,
169
+				raw_menu_prompt, raw_prompt_len );
170
+		(*menu)->timeout =
171
+			( ( raw_menu_prompt->timeout == 0xff ) ?
172
+			  -1 : raw_menu_prompt->timeout );
173
+		(*menu)->prompt = raw_menu_prompt->prompt;
174
+	} else {
175
+		(*menu)->timeout = -1;
176
+	}
156
 
177
 
157
 	return 0;
178
 	return 0;
158
 }
179
 }
162
  *
183
  *
163
  * @v menu		PXE boot menu
184
  * @v menu		PXE boot menu
164
  * @v index		Index of item to draw
185
  * @v index		Index of item to draw
186
+ * @v selected		Item is selected
165
  */
187
  */
166
 static void pxe_menu_draw_item ( struct pxe_menu *menu,
188
 static void pxe_menu_draw_item ( struct pxe_menu *menu,
167
-				 unsigned int index ) {
168
-	int selected = ( menu->selection == index );
189
+				 unsigned int index, int selected ) {
169
 	char buf[COLS+1];
190
 	char buf[COLS+1];
170
-	char *tmp = buf;
171
-	ssize_t remaining = sizeof ( buf );
172
 	size_t len;
191
 	size_t len;
173
 	unsigned int row;
192
 	unsigned int row;
174
 
193
 
175
 	/* Prepare space-padded row content */
194
 	/* Prepare space-padded row content */
176
-	len = ssnprintf ( tmp, remaining, " %c. %s",
177
-			  ( 'A' + index ), menu->items[index].desc );
178
-	tmp += len;
179
-	remaining -= len;
180
-	if ( selected && ( menu->timeout > 0 ) ) {
181
-		len = ssnprintf ( tmp, remaining, " (%d)", menu->timeout );
182
-		tmp += len;
183
-		remaining -= len;
184
-	}
185
-	for ( ; remaining > 1 ; tmp++, remaining-- )
186
-		*tmp = ' ';
187
-	*tmp = '\0';
195
+	len = snprintf ( buf, sizeof ( buf ), " %c. %s",
196
+			 ( 'A' + index ), menu->items[index].desc );
197
+	while ( len < ( sizeof ( buf ) - 1 ) )
198
+		buf[len++] = ' ';
199
+	buf[ sizeof ( buf ) - 1 ] = '\0';
188
 
200
 
189
 	/* Draw row */
201
 	/* Draw row */
190
 	row = ( LINES - menu->num_items + index );
202
 	row = ( LINES - menu->num_items + index );
199
  * @v menu		PXE boot menu
211
  * @v menu		PXE boot menu
200
  * @ret rc		Return status code
212
  * @ret rc		Return status code
201
  */
213
  */
202
-int pxe_menu_select ( struct pxe_menu *menu ) {
203
-	unsigned long start = currticks();
204
-	unsigned long now;
205
-	unsigned long elapsed;
206
-	unsigned int old_selection;
214
+static int pxe_menu_select ( struct pxe_menu *menu ) {
207
 	int key;
215
 	int key;
208
 	unsigned int key_selection;
216
 	unsigned int key_selection;
209
 	unsigned int i;
217
 	unsigned int i;
220
 	for ( i = 0 ; i < menu->num_items ; i++ )
228
 	for ( i = 0 ; i < menu->num_items ; i++ )
221
 		printf ( "\n" );
229
 		printf ( "\n" );
222
 	for ( i = 0 ; i < menu->num_items ; i++ )
230
 	for ( i = 0 ; i < menu->num_items ; i++ )
223
-		pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ) );
231
+		pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 );
224
 
232
 
225
 	while ( 1 ) {
233
 	while ( 1 ) {
226
 
234
 
227
-		/* Decrease timeout if necessary */
228
-		if ( menu->timeout > 0 ) {
229
-			now = currticks();
230
-			elapsed = ( now - start );
231
-			if ( elapsed >= TICKS_PER_SEC ) {
232
-				start = now;
233
-				menu->timeout--;
234
-				pxe_menu_draw_item ( menu, menu->selection );
235
-			}
236
-		}
237
-
238
-		/* Select current item if we have timed out */
239
-		if ( menu->timeout == 0 )
240
-			break;
235
+		/* Highlight currently selected item */
236
+		pxe_menu_draw_item ( menu, menu->selection, 1 );
241
 
237
 
242
-		/* Check for keyboard input */
243
-		if ( ! iskey() )
244
-			continue;
238
+		/* Wait for keyboard input */
239
+		while ( ! iskey() )
240
+			step();
245
 		key = getkey();
241
 		key = getkey();
246
 
242
 
247
-		/* Any keyboard input cancels the timeout */
248
-		menu->timeout = -1;
249
-		pxe_menu_draw_item ( menu, menu->selection );
243
+		/* Unhighlight currently selected item */
244
+		pxe_menu_draw_item ( menu, menu->selection, 0 );
250
 
245
 
251
 		/* Act upon key */
246
 		/* Act upon key */
252
-		old_selection = menu->selection;
253
 		if ( ( key == CR ) || ( key == LF ) ) {
247
 		if ( ( key == CR ) || ( key == LF ) ) {
248
+			pxe_menu_draw_item ( menu, menu->selection, 1 );
254
 			break;
249
 			break;
255
 		} else if ( key == CTRL_C ) {
250
 		} else if ( key == CTRL_C ) {
256
 			rc = -ECANCELED;
251
 			rc = -ECANCELED;
265
 			    ( ( key_selection = ( toupper ( key ) - 'A' ) )
260
 			    ( ( key_selection = ( toupper ( key ) - 'A' ) )
266
 			      < menu->num_items ) ) {
261
 			      < menu->num_items ) ) {
267
 			menu->selection = key_selection;
262
 			menu->selection = key_selection;
268
-			menu->timeout = 0;
263
+			pxe_menu_draw_item ( menu, menu->selection, 1 );
264
+			break;
269
 		}
265
 		}
270
-		pxe_menu_draw_item ( menu, old_selection );
271
-		pxe_menu_draw_item ( menu, menu->selection );
272
 	}
266
 	}
273
 
267
 
274
 	/* Shut down UI */
268
 	/* Shut down UI */
277
 	return rc;
271
 	return rc;
278
 }
272
 }
279
 
273
 
274
+/**
275
+ * Prompt for (and make selection from) PXE boot menu
276
+ *
277
+ * @v menu		PXE boot menu
278
+ * @ret rc		Return status code
279
+ */
280
+static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) {
281
+	unsigned long start = currticks();
282
+	unsigned long now;
283
+	unsigned long elapsed;
284
+	size_t len = 0;
285
+	int key;
286
+	int rc = 0;
287
+
288
+	/* Display menu immediately, if specified to do so */
289
+	if ( menu->timeout < 0 ) {
290
+		if ( menu->prompt )
291
+			printf ( "%s\n", menu->prompt );
292
+		return pxe_menu_select ( menu );
293
+	}
294
+
295
+	/* Display prompt, if specified */
296
+	if ( menu->prompt )
297
+		printf ( "%s", menu->prompt );
298
+
299
+	/* Wait for timeout, if specified */
300
+	while ( menu->timeout > 0 ) {
301
+		if ( ! len )
302
+			len = printf ( " (%d)", menu->timeout );
303
+		if ( iskey() ) {
304
+			key = getkey();
305
+			if ( key == KEY_F8 ) {
306
+				/* Display menu */
307
+				printf ( "\n" );
308
+				return pxe_menu_select ( menu );
309
+			} else if ( key == CTRL_C ) {
310
+				/* Abort */
311
+				rc = -ECANCELED;
312
+				break;
313
+			} else {
314
+				/* Stop waiting */
315
+				break;
316
+			}
317
+		}
318
+		now = currticks();
319
+		elapsed = ( now - start );
320
+		if ( elapsed >= TICKS_PER_SEC ) {
321
+			menu->timeout -= 1;
322
+			do {
323
+				printf ( "\b \b" );
324
+			} while ( --len );
325
+			start = now;
326
+		}
327
+	}
328
+
329
+	/* Return with default option selected */
330
+	printf ( "\n" );
331
+	return rc;
332
+}
333
+
280
 /**
334
 /**
281
  * Boot using PXE boot menu
335
  * Boot using PXE boot menu
282
  *
336
  *
299
 		return rc;
353
 		return rc;
300
 
354
 
301
 	/* Make selection from boot menu */
355
 	/* Make selection from boot menu */
302
-	if ( ( rc = pxe_menu_select ( menu ) ) != 0 ) {
356
+	if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) {
303
 		free ( menu );
357
 		free ( menu );
304
 		return rc;
358
 		return rc;
305
 	}
359
 	}

Loading…
Cancel
Save