Annotation of qemu/roms/ipxe/src/usr/pxemenu.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.