00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021 #include <stdint.h>
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <string.h>
00025 #include <errno.h>
00026 #include <ctype.h>
00027 #include <byteswap.h>
00028 #include <curses.h>
00029 #include <console.h>
00030 #include <gpxe/dhcp.h>
00031 #include <gpxe/keys.h>
00032 #include <gpxe/timer.h>
00033 #include <gpxe/process.h>
00034 #include <usr/dhcpmgmt.h>
00035 #include <usr/autoboot.h>
00036
00037
00038
00039
00040
00041
00042
00043
00044 #define CPAIR_NORMAL 1
00045 #define CPAIR_SELECT 2
00046
00047
00048 struct pxe_menu_item {
00049
00050 unsigned int type;
00051
00052 char *desc;
00053 };
00054
00055
00056
00057
00058
00059
00060
00061 struct pxe_menu {
00062
00063 const char *prompt;
00064
00065
00066
00067
00068 int timeout;
00069
00070 unsigned int num_items;
00071
00072 unsigned int selection;
00073
00074 struct pxe_menu_item items[0];
00075 };
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086 static int pxe_menu_parse ( struct pxe_menu **menu ) {
00087 struct setting pxe_boot_menu_prompt_setting =
00088 { .tag = DHCP_PXE_BOOT_MENU_PROMPT };
00089 struct setting pxe_boot_menu_setting =
00090 { .tag = DHCP_PXE_BOOT_MENU };
00091 uint8_t raw_menu[256];
00092 int raw_prompt_len;
00093 int raw_menu_len;
00094 struct dhcp_pxe_boot_menu *raw_menu_item;
00095 struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt;
00096 void *raw_menu_end;
00097 unsigned int num_menu_items;
00098 unsigned int i;
00099 int rc;
00100
00101
00102 memset ( raw_menu, 0, sizeof ( raw_menu ) );
00103 if ( ( raw_menu_len = fetch_setting ( NULL, &pxe_boot_menu_setting,
00104 raw_menu,
00105 sizeof ( raw_menu ) ) ) < 0 ) {
00106 rc = raw_menu_len;
00107 DBG ( "Could not retrieve raw PXE boot menu: %s\n",
00108 strerror ( rc ) );
00109 return rc;
00110 }
00111 if ( raw_menu_len >= ( int ) sizeof ( raw_menu ) ) {
00112 DBG ( "Raw PXE boot menu too large for buffer\n" );
00113 return -ENOSPC;
00114 }
00115 raw_menu_end = ( raw_menu + raw_menu_len );
00116
00117
00118 raw_prompt_len = fetch_setting_len ( NULL,
00119 &pxe_boot_menu_prompt_setting );
00120 if ( raw_prompt_len < 0 )
00121 raw_prompt_len = 0;
00122
00123
00124 num_menu_items = 0;
00125 raw_menu_item = ( ( void * ) raw_menu );
00126 while ( 1 ) {
00127 if ( ( ( ( void * ) raw_menu_item ) +
00128 sizeof ( *raw_menu_item ) ) > raw_menu_end )
00129 break;
00130 if ( ( ( ( void * ) raw_menu_item ) +
00131 sizeof ( *raw_menu_item ) +
00132 raw_menu_item->desc_len ) > raw_menu_end )
00133 break;
00134 num_menu_items++;
00135 raw_menu_item = ( ( ( void * ) raw_menu_item ) +
00136 sizeof ( *raw_menu_item ) +
00137 raw_menu_item->desc_len );
00138 }
00139
00140
00141 *menu = zalloc ( sizeof ( **menu ) +
00142 ( num_menu_items * sizeof ( (*menu)->items[0] ) ) +
00143 raw_menu_len + 1 +
00144 raw_prompt_len + 1 );
00145 if ( ! *menu ) {
00146 DBG ( "Could not allocate PXE boot menu\n" );
00147 return -ENOMEM;
00148 }
00149
00150
00151 (*menu)->num_items = num_menu_items;
00152 raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
00153 ( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
00154 memcpy ( raw_menu_item, raw_menu, raw_menu_len );
00155 for ( i = 0 ; i < num_menu_items ; i++ ) {
00156 (*menu)->items[i].type = le16_to_cpu ( raw_menu_item->type );
00157 (*menu)->items[i].desc = raw_menu_item->desc;
00158
00159
00160
00161
00162 raw_menu_item->type = 0;
00163 raw_menu_item = ( ( ( void * ) raw_menu_item ) +
00164 sizeof ( *raw_menu_item ) +
00165 raw_menu_item->desc_len );
00166 }
00167 if ( raw_prompt_len ) {
00168 raw_menu_prompt = ( ( ( void * ) raw_menu_item ) +
00169 1 );
00170 fetch_setting ( NULL, &pxe_boot_menu_prompt_setting,
00171 raw_menu_prompt, raw_prompt_len );
00172 (*menu)->timeout =
00173 ( ( raw_menu_prompt->timeout == 0xff ) ?
00174 -1 : raw_menu_prompt->timeout );
00175 (*menu)->prompt = raw_menu_prompt->prompt;
00176 } else {
00177 (*menu)->timeout = -1;
00178 }
00179
00180 return 0;
00181 }
00182
00183
00184
00185
00186
00187
00188
00189
00190 static void pxe_menu_draw_item ( struct pxe_menu *menu,
00191 unsigned int index, int selected ) {
00192 char buf[COLS+1];
00193 size_t len;
00194 unsigned int row;
00195
00196
00197 len = snprintf ( buf, sizeof ( buf ), " %c. %s",
00198 ( 'A' + index ), menu->items[index].desc );
00199 while ( len < ( sizeof ( buf ) - 1 ) )
00200 buf[len++] = ' ';
00201 buf[ sizeof ( buf ) - 1 ] = '\0';
00202
00203
00204 row = ( LINES - menu->num_items + index );
00205 color_set ( ( selected ? CPAIR_SELECT : CPAIR_NORMAL ), NULL );
00206 mvprintw ( row, 0, "%s", buf );
00207 move ( row, 1 );
00208 }
00209
00210
00211
00212
00213
00214
00215
00216 static int pxe_menu_select ( struct pxe_menu *menu ) {
00217 int key;
00218 unsigned int key_selection;
00219 unsigned int i;
00220 int rc = 0;
00221
00222
00223 initscr();
00224 start_color();
00225 init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLACK );
00226 init_pair ( CPAIR_SELECT, COLOR_BLACK, COLOR_WHITE );
00227 color_set ( CPAIR_NORMAL, NULL );
00228
00229
00230 for ( i = 0 ; i < menu->num_items ; i++ )
00231 printf ( "\n" );
00232 for ( i = 0 ; i < menu->num_items ; i++ )
00233 pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 );
00234
00235 while ( 1 ) {
00236
00237
00238 pxe_menu_draw_item ( menu, menu->selection, 1 );
00239
00240
00241 while ( ! iskey() )
00242 step();
00243 key = getkey();
00244
00245
00246 pxe_menu_draw_item ( menu, menu->selection, 0 );
00247
00248
00249 if ( ( key == CR ) || ( key == LF ) ) {
00250 pxe_menu_draw_item ( menu, menu->selection, 1 );
00251 break;
00252 } else if ( ( key == CTRL_C ) || ( key == ESC ) ) {
00253 rc = -ECANCELED;
00254 break;
00255 } else if ( key == KEY_UP ) {
00256 if ( menu->selection > 0 )
00257 menu->selection--;
00258 } else if ( key == KEY_DOWN ) {
00259 if ( menu->selection < ( menu->num_items - 1 ) )
00260 menu->selection++;
00261 } else if ( ( key < KEY_MIN ) &&
00262 ( ( key_selection = ( toupper ( key ) - 'A' ) )
00263 < menu->num_items ) ) {
00264 menu->selection = key_selection;
00265 pxe_menu_draw_item ( menu, menu->selection, 1 );
00266 break;
00267 }
00268 }
00269
00270
00271 endwin();
00272
00273 return rc;
00274 }
00275
00276
00277
00278
00279
00280
00281
00282 static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) {
00283 unsigned long start = currticks();
00284 unsigned long now;
00285 unsigned long elapsed;
00286 size_t len = 0;
00287 int key;
00288 int rc = 0;
00289
00290
00291 if ( menu->timeout < 0 ) {
00292 if ( menu->prompt )
00293 printf ( "%s\n", menu->prompt );
00294 return pxe_menu_select ( menu );
00295 }
00296
00297
00298 if ( menu->prompt )
00299 printf ( "%s", menu->prompt );
00300
00301
00302 while ( menu->timeout > 0 ) {
00303 if ( ! len )
00304 len = printf ( " (%d)", menu->timeout );
00305 if ( iskey() ) {
00306 key = getkey();
00307 if ( key == KEY_F8 ) {
00308
00309 printf ( "\n" );
00310 return pxe_menu_select ( menu );
00311 } else if ( ( key == CTRL_C ) || ( key == ESC ) ) {
00312
00313 rc = -ECANCELED;
00314 break;
00315 } else {
00316
00317 break;
00318 }
00319 }
00320 now = currticks();
00321 elapsed = ( now - start );
00322 if ( elapsed >= TICKS_PER_SEC ) {
00323 menu->timeout -= 1;
00324 do {
00325 printf ( "\b \b" );
00326 } while ( --len );
00327 start = now;
00328 }
00329 }
00330
00331
00332 printf ( "\n" );
00333 return rc;
00334 }
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345 int pxe_menu_boot ( struct net_device *netdev ) {
00346 struct pxe_menu *menu;
00347 unsigned int pxe_type;
00348 struct settings *pxebs_settings;
00349 struct in_addr next_server;
00350 char filename[256];
00351 int rc;
00352
00353
00354 if ( ( rc = pxe_menu_parse ( &menu ) ) != 0 )
00355 return rc;
00356
00357
00358 if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) {
00359 free ( menu );
00360 return rc;
00361 }
00362 pxe_type = menu->items[menu->selection].type;
00363
00364
00365 free ( menu );
00366
00367
00368 if ( ! pxe_type )
00369 return 0;
00370
00371
00372 if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 )
00373 return rc;
00374
00375
00376 pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME );
00377 assert ( pxebs_settings );
00378 fetch_ipv4_setting ( pxebs_settings, &next_server_setting,
00379 &next_server );
00380 fetch_string_setting ( pxebs_settings, &filename_setting,
00381 filename, sizeof ( filename ) );
00382 return boot_next_server_and_filename ( next_server, filename );
00383 }