|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2009 Michael Brown <[email protected]>. ! 3: * ! 4: * This program is free software; you can redistribute it and/or ! 5: * modify it under the terms of the GNU General Public License as ! 6: * published by the Free Software Foundation; either version 2 of the ! 7: * License, or any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, but ! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 12: * General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program; if not, write to the Free Software ! 16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: #include <stdint.h> ! 22: #include <stdlib.h> ! 23: #include <stdio.h> ! 24: #include <string.h> ! 25: #include <errno.h> ! 26: #include <ctype.h> ! 27: #include <byteswap.h> ! 28: #include <curses.h> ! 29: #include <ipxe/console.h> ! 30: #include <ipxe/dhcp.h> ! 31: #include <ipxe/keys.h> ! 32: #include <ipxe/timer.h> ! 33: #include <ipxe/uri.h> ! 34: #include <usr/dhcpmgmt.h> ! 35: #include <usr/autoboot.h> ! 36: ! 37: /** @file ! 38: * ! 39: * PXE Boot Menus ! 40: * ! 41: */ ! 42: ! 43: /* Colour pairs */ ! 44: #define CPAIR_NORMAL 1 ! 45: #define CPAIR_SELECT 2 ! 46: ! 47: /** A PXE boot menu item */ ! 48: struct pxe_menu_item { ! 49: /** Boot Server type */ ! 50: unsigned int type; ! 51: /** Description */ ! 52: char *desc; ! 53: }; ! 54: ! 55: /** ! 56: * A PXE boot menu ! 57: * ! 58: * This structure encapsulates the menu information provided via DHCP ! 59: * options. ! 60: */ ! 61: struct pxe_menu { ! 62: /** Prompt string (optional) */ ! 63: const char *prompt; ! 64: /** Timeout (in seconds) ! 65: * ! 66: * Negative indicates no timeout (i.e. wait indefinitely) ! 67: */ ! 68: int timeout; ! 69: /** Number of menu items */ ! 70: unsigned int num_items; ! 71: /** Selected menu item */ ! 72: unsigned int selection; ! 73: /** Menu items */ ! 74: struct pxe_menu_item items[0]; ! 75: }; ! 76: ! 77: /** ! 78: * Parse and allocate PXE boot menu ! 79: * ! 80: * @v menu PXE boot menu to fill in ! 81: * @ret rc Return status code ! 82: * ! 83: * It is the callers responsibility to eventually free the allocated ! 84: * boot menu. ! 85: */ ! 86: static int pxe_menu_parse ( struct pxe_menu **menu ) { ! 87: struct setting pxe_boot_menu_prompt_setting = ! 88: { .tag = DHCP_PXE_BOOT_MENU_PROMPT }; ! 89: struct setting pxe_boot_menu_setting = ! 90: { .tag = DHCP_PXE_BOOT_MENU }; ! 91: uint8_t raw_menu[256]; ! 92: int raw_prompt_len; ! 93: int raw_menu_len; ! 94: struct dhcp_pxe_boot_menu *raw_menu_item; ! 95: struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt; ! 96: void *raw_menu_end; ! 97: unsigned int num_menu_items; ! 98: unsigned int i; ! 99: int rc; ! 100: ! 101: /* Fetch raw menu */ ! 102: memset ( raw_menu, 0, sizeof ( raw_menu ) ); ! 103: if ( ( raw_menu_len = fetch_setting ( NULL, &pxe_boot_menu_setting, ! 104: raw_menu, ! 105: sizeof ( raw_menu ) ) ) < 0 ) { ! 106: rc = raw_menu_len; ! 107: DBG ( "Could not retrieve raw PXE boot menu: %s\n", ! 108: strerror ( rc ) ); ! 109: return rc; ! 110: } ! 111: if ( raw_menu_len >= ( int ) sizeof ( raw_menu ) ) { ! 112: DBG ( "Raw PXE boot menu too large for buffer\n" ); ! 113: return -ENOSPC; ! 114: } ! 115: raw_menu_end = ( raw_menu + raw_menu_len ); ! 116: ! 117: /* Fetch raw prompt length */ ! 118: raw_prompt_len = fetch_setting_len ( NULL, ! 119: &pxe_boot_menu_prompt_setting ); ! 120: if ( raw_prompt_len < 0 ) ! 121: raw_prompt_len = 0; ! 122: ! 123: /* Count menu items */ ! 124: num_menu_items = 0; ! 125: raw_menu_item = ( ( void * ) raw_menu ); ! 126: while ( 1 ) { ! 127: if ( ( ( ( void * ) raw_menu_item ) + ! 128: sizeof ( *raw_menu_item ) ) > raw_menu_end ) ! 129: break; ! 130: if ( ( ( ( void * ) raw_menu_item ) + ! 131: sizeof ( *raw_menu_item ) + ! 132: raw_menu_item->desc_len ) > raw_menu_end ) ! 133: break; ! 134: num_menu_items++; ! 135: raw_menu_item = ( ( ( void * ) raw_menu_item ) + ! 136: sizeof ( *raw_menu_item ) + ! 137: raw_menu_item->desc_len ); ! 138: } ! 139: ! 140: /* Allocate space for parsed menu */ ! 141: *menu = zalloc ( sizeof ( **menu ) + ! 142: ( num_menu_items * sizeof ( (*menu)->items[0] ) ) + ! 143: raw_menu_len + 1 /* NUL */ + ! 144: raw_prompt_len + 1 /* NUL */ ); ! 145: if ( ! *menu ) { ! 146: DBG ( "Could not allocate PXE boot menu\n" ); ! 147: return -ENOMEM; ! 148: } ! 149: ! 150: /* Fill in parsed menu */ ! 151: (*menu)->num_items = num_menu_items; ! 152: raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) + ! 153: ( num_menu_items * sizeof ( (*menu)->items[0] ) ) ); ! 154: memcpy ( raw_menu_item, raw_menu, raw_menu_len ); ! 155: for ( i = 0 ; i < num_menu_items ; i++ ) { ! 156: (*menu)->items[i].type = le16_to_cpu ( raw_menu_item->type ); ! 157: (*menu)->items[i].desc = raw_menu_item->desc; ! 158: /* Set type to 0; this ensures that the description ! 159: * for the previous menu item is NUL-terminated. ! 160: * (Final item is NUL-terminated anyway.) ! 161: */ ! 162: raw_menu_item->type = 0; ! 163: raw_menu_item = ( ( ( void * ) raw_menu_item ) + ! 164: sizeof ( *raw_menu_item ) + ! 165: raw_menu_item->desc_len ); ! 166: } ! 167: if ( raw_prompt_len ) { ! 168: raw_menu_prompt = ( ( ( void * ) raw_menu_item ) + ! 169: 1 /* NUL */ ); ! 170: fetch_setting ( NULL, &pxe_boot_menu_prompt_setting, ! 171: raw_menu_prompt, raw_prompt_len ); ! 172: (*menu)->timeout = ! 173: ( ( raw_menu_prompt->timeout == 0xff ) ? ! 174: -1 : raw_menu_prompt->timeout ); ! 175: (*menu)->prompt = raw_menu_prompt->prompt; ! 176: } else { ! 177: (*menu)->timeout = -1; ! 178: } ! 179: ! 180: return 0; ! 181: } ! 182: ! 183: /** ! 184: * Draw PXE boot menu item ! 185: * ! 186: * @v menu PXE boot menu ! 187: * @v index Index of item to draw ! 188: * @v selected Item is selected ! 189: */ ! 190: static void pxe_menu_draw_item ( struct pxe_menu *menu, ! 191: unsigned int index, int selected ) { ! 192: char buf[COLS+1]; ! 193: size_t len; ! 194: unsigned int row; ! 195: ! 196: /* Prepare space-padded row content */ ! 197: len = snprintf ( buf, sizeof ( buf ), " %c. %s", ! 198: ( 'A' + index ), menu->items[index].desc ); ! 199: while ( len < ( sizeof ( buf ) - 1 ) ) ! 200: buf[len++] = ' '; ! 201: buf[ sizeof ( buf ) - 1 ] = '\0'; ! 202: ! 203: /* Draw row */ ! 204: row = ( LINES - menu->num_items + index ); ! 205: color_set ( ( selected ? CPAIR_SELECT : CPAIR_NORMAL ), NULL ); ! 206: mvprintw ( row, 0, "%s", buf ); ! 207: move ( row, 1 ); ! 208: } ! 209: ! 210: /** ! 211: * Make selection from PXE boot menu ! 212: * ! 213: * @v menu PXE boot menu ! 214: * @ret rc Return status code ! 215: */ ! 216: static int pxe_menu_select ( struct pxe_menu *menu ) { ! 217: int key; ! 218: unsigned int key_selection; ! 219: unsigned int i; ! 220: int rc = 0; ! 221: ! 222: /* Initialise UI */ ! 223: initscr(); ! 224: start_color(); ! 225: init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); ! 226: init_pair ( CPAIR_SELECT, COLOR_BLACK, COLOR_WHITE ); ! 227: color_set ( CPAIR_NORMAL, NULL ); ! 228: ! 229: /* Draw initial menu */ ! 230: for ( i = 0 ; i < menu->num_items ; i++ ) ! 231: printf ( "\n" ); ! 232: for ( i = 0 ; i < menu->num_items ; i++ ) ! 233: pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 ); ! 234: ! 235: while ( 1 ) { ! 236: ! 237: /* Highlight currently selected item */ ! 238: pxe_menu_draw_item ( menu, menu->selection, 1 ); ! 239: ! 240: /* Wait for keyboard input */ ! 241: key = getkey ( 0 ); ! 242: ! 243: /* Unhighlight currently selected item */ ! 244: pxe_menu_draw_item ( menu, menu->selection, 0 ); ! 245: ! 246: /* Act upon key */ ! 247: if ( ( key == CR ) || ( key == LF ) ) { ! 248: pxe_menu_draw_item ( menu, menu->selection, 1 ); ! 249: break; ! 250: } else if ( ( key == CTRL_C ) || ( key == ESC ) ) { ! 251: rc = -ECANCELED; ! 252: break; ! 253: } else if ( key == KEY_UP ) { ! 254: if ( menu->selection > 0 ) ! 255: menu->selection--; ! 256: } else if ( key == KEY_DOWN ) { ! 257: if ( menu->selection < ( menu->num_items - 1 ) ) ! 258: menu->selection++; ! 259: } else if ( ( key < KEY_MIN ) && ! 260: ( ( key_selection = ( toupper ( key ) - 'A' ) ) ! 261: < menu->num_items ) ) { ! 262: menu->selection = key_selection; ! 263: pxe_menu_draw_item ( menu, menu->selection, 1 ); ! 264: break; ! 265: } ! 266: } ! 267: ! 268: /* Shut down UI */ ! 269: endwin(); ! 270: ! 271: return rc; ! 272: } ! 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 ( 0 ); ! 305: if ( key == KEY_F8 ) { ! 306: /* Display menu */ ! 307: printf ( "\n" ); ! 308: return pxe_menu_select ( menu ); ! 309: } else if ( ( key == CTRL_C ) || ( key == ESC ) ) { ! 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: ! 334: /** ! 335: * Boot using PXE boot menu ! 336: * ! 337: * @ret rc Return status code ! 338: * ! 339: * Note that a success return status indicates that a PXE boot menu ! 340: * item has been selected, and that the DHCP session should perform a ! 341: * boot server request/ack. ! 342: */ ! 343: int pxe_menu_boot ( struct net_device *netdev ) { ! 344: struct pxe_menu *menu; ! 345: unsigned int pxe_type; ! 346: struct settings *pxebs_settings; ! 347: struct uri *uri; ! 348: int rc; ! 349: ! 350: /* Parse and allocate boot menu */ ! 351: if ( ( rc = pxe_menu_parse ( &menu ) ) != 0 ) ! 352: return rc; ! 353: ! 354: /* Make selection from boot menu */ ! 355: if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) { ! 356: free ( menu ); ! 357: return rc; ! 358: } ! 359: pxe_type = menu->items[menu->selection].type; ! 360: ! 361: /* Free boot menu */ ! 362: free ( menu ); ! 363: ! 364: /* Return immediately if local boot selected */ ! 365: if ( ! pxe_type ) ! 366: return 0; ! 367: ! 368: /* Attempt PXE Boot Server Discovery */ ! 369: if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 ) ! 370: return rc; ! 371: ! 372: /* Fetch next server and filename */ ! 373: pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); ! 374: assert ( pxebs_settings ); ! 375: uri = fetch_next_server_and_filename ( pxebs_settings ); ! 376: if ( ! uri ) ! 377: return -ENOMEM; ! 378: ! 379: /* Attempt boot */ ! 380: rc = uriboot ( uri, NULL ); ! 381: uri_put ( uri ); ! 382: return rc; ! 383: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.