|
@@ -26,9 +26,9 @@
|
26
|
26
|
#include <curses.h>
|
27
|
27
|
#include <console.h>
|
28
|
28
|
#include <gpxe/dhcp.h>
|
29
|
|
-#include <gpxe/vsprintf.h>
|
30
|
29
|
#include <gpxe/keys.h>
|
31
|
30
|
#include <gpxe/timer.h>
|
|
31
|
+#include <gpxe/process.h>
|
32
|
32
|
#include <usr/dhcpmgmt.h>
|
33
|
33
|
#include <usr/autoboot.h>
|
34
|
34
|
|
|
@@ -57,6 +57,8 @@ struct pxe_menu_item {
|
57
|
57
|
* options.
|
58
|
58
|
*/
|
59
|
59
|
struct pxe_menu {
|
|
60
|
+ /** Prompt string (optional) */
|
|
61
|
+ const char *prompt;
|
60
|
62
|
/** Timeout (in seconds)
|
61
|
63
|
*
|
62
|
64
|
* Negative indicates no timeout (i.e. wait indefinitely)
|
|
@@ -80,22 +82,24 @@ struct pxe_menu {
|
80
|
82
|
* boot menu.
|
81
|
83
|
*/
|
82
|
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
|
89
|
uint8_t raw_menu[256];
|
|
90
|
+ int raw_prompt_len;
|
86
|
91
|
int raw_menu_len;
|
87
|
92
|
struct dhcp_pxe_boot_menu *raw_menu_item;
|
|
93
|
+ struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt;
|
88
|
94
|
void *raw_menu_end;
|
89
|
95
|
unsigned int num_menu_items;
|
90
|
96
|
unsigned int i;
|
91
|
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
|
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
|
103
|
sizeof ( raw_menu ) ) ) < 0 ) {
|
100
|
104
|
rc = raw_menu_len;
|
101
|
105
|
DBG ( "Could not retrieve raw PXE boot menu: %s\n",
|
|
@@ -108,6 +112,12 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
|
108
|
112
|
}
|
109
|
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
|
121
|
/* Count menu items */
|
112
|
122
|
num_menu_items = 0;
|
113
|
123
|
raw_menu_item = ( ( void * ) raw_menu );
|
|
@@ -128,15 +138,14 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
|
128
|
138
|
/* Allocate space for parsed menu */
|
129
|
139
|
*menu = zalloc ( sizeof ( **menu ) +
|
130
|
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
|
143
|
if ( ! *menu ) {
|
133
|
144
|
DBG ( "Could not allocate PXE boot menu\n" );
|
134
|
145
|
return -ENOMEM;
|
135
|
146
|
}
|
136
|
147
|
|
137
|
148
|
/* Fill in parsed menu */
|
138
|
|
- (*menu)->timeout =
|
139
|
|
- ( ( prompt.timeout == 0xff ) ? -1 : prompt.timeout );
|
140
|
149
|
(*menu)->num_items = num_menu_items;
|
141
|
150
|
raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
|
142
|
151
|
( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
|
|
@@ -153,6 +162,18 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
|
153
|
162
|
sizeof ( *raw_menu_item ) +
|
154
|
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
|
178
|
return 0;
|
158
|
179
|
}
|
|
@@ -162,29 +183,20 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
|
162
|
183
|
*
|
163
|
184
|
* @v menu PXE boot menu
|
164
|
185
|
* @v index Index of item to draw
|
|
186
|
+ * @v selected Item is selected
|
165
|
187
|
*/
|
166
|
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
|
190
|
char buf[COLS+1];
|
170
|
|
- char *tmp = buf;
|
171
|
|
- ssize_t remaining = sizeof ( buf );
|
172
|
191
|
size_t len;
|
173
|
192
|
unsigned int row;
|
174
|
193
|
|
175
|
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
|
201
|
/* Draw row */
|
190
|
202
|
row = ( LINES - menu->num_items + index );
|
|
@@ -199,11 +211,7 @@ static void pxe_menu_draw_item ( struct pxe_menu *menu,
|
199
|
211
|
* @v menu PXE boot menu
|
200
|
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
|
215
|
int key;
|
208
|
216
|
unsigned int key_selection;
|
209
|
217
|
unsigned int i;
|
|
@@ -220,37 +228,24 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
|
220
|
228
|
for ( i = 0 ; i < menu->num_items ; i++ )
|
221
|
229
|
printf ( "\n" );
|
222
|
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
|
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
|
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
|
246
|
/* Act upon key */
|
252
|
|
- old_selection = menu->selection;
|
253
|
247
|
if ( ( key == CR ) || ( key == LF ) ) {
|
|
248
|
+ pxe_menu_draw_item ( menu, menu->selection, 1 );
|
254
|
249
|
break;
|
255
|
250
|
} else if ( key == CTRL_C ) {
|
256
|
251
|
rc = -ECANCELED;
|
|
@@ -265,10 +260,9 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
|
265
|
260
|
( ( key_selection = ( toupper ( key ) - 'A' ) )
|
266
|
261
|
< menu->num_items ) ) {
|
267
|
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
|
268
|
/* Shut down UI */
|
|
@@ -277,6 +271,66 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
|
277
|
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
|
335
|
* Boot using PXE boot menu
|
282
|
336
|
*
|
|
@@ -299,7 +353,7 @@ int pxe_menu_boot ( struct net_device *netdev ) {
|
299
|
353
|
return rc;
|
300
|
354
|
|
301
|
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
|
357
|
free ( menu );
|
304
|
358
|
return rc;
|
305
|
359
|
}
|