Annotation of qemu/roms/ipxe/src/usr/autoboot.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (C) 2006 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 <string.h>
        !            22: #include <stdio.h>
        !            23: #include <errno.h>
        !            24: #include <ipxe/netdevice.h>
        !            25: #include <ipxe/dhcp.h>
        !            26: #include <ipxe/settings.h>
        !            27: #include <ipxe/image.h>
        !            28: #include <ipxe/sanboot.h>
        !            29: #include <ipxe/uri.h>
        !            30: #include <ipxe/open.h>
        !            31: #include <ipxe/init.h>
        !            32: #include <usr/ifmgmt.h>
        !            33: #include <usr/route.h>
        !            34: #include <usr/dhcpmgmt.h>
        !            35: #include <usr/imgmgmt.h>
        !            36: #include <usr/autoboot.h>
        !            37: 
        !            38: /** @file
        !            39:  *
        !            40:  * Automatic booting
        !            41:  *
        !            42:  */
        !            43: 
        !            44: /* Disambiguate the various error causes */
        !            45: #define ENOENT_BOOT __einfo_error ( EINFO_ENOENT_BOOT )
        !            46: #define EINFO_ENOENT_BOOT \
        !            47:        __einfo_uniqify ( EINFO_ENOENT, 0x01, "Nothing to boot" )
        !            48: 
        !            49: /**
        !            50:  * Perform PXE menu boot when PXE stack is not available
        !            51:  */
        !            52: __weak int pxe_menu_boot ( struct net_device *netdev __unused ) {
        !            53:        return -ENOTSUP;
        !            54: }
        !            55: 
        !            56: /**
        !            57:  * Identify the boot network device
        !            58:  *
        !            59:  * @ret netdev         Boot network device
        !            60:  */
        !            61: static struct net_device * find_boot_netdev ( void ) {
        !            62:        return NULL;
        !            63: }
        !            64: 
        !            65: /**
        !            66:  * Parse next-server and filename into a URI
        !            67:  *
        !            68:  * @v next_server      Next-server address
        !            69:  * @v filename         Filename
        !            70:  * @ret uri            URI, or NULL on failure
        !            71:  */
        !            72: static struct uri * parse_next_server_and_filename ( struct in_addr next_server,
        !            73:                                                     const char *filename ) {
        !            74:        char buf[ 23 /* "tftp://xxx.xxx.xxx.xxx/" */ + strlen ( filename )
        !            75:                  + 1 /* NUL */ ];
        !            76:        struct uri *uri;
        !            77: 
        !            78:        /* Parse filename */
        !            79:        uri = parse_uri ( filename );
        !            80:        if ( ! uri )
        !            81:                return NULL;
        !            82: 
        !            83:        /* Construct a tftp:// URI for the filename, if applicable.
        !            84:         * We can't just rely on the current working URI, because the
        !            85:         * relative URI resolution will remove the distinction between
        !            86:         * filenames with and without initial slashes, which is
        !            87:         * significant for TFTP.
        !            88:         */
        !            89:        if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) {
        !            90:                uri_put ( uri );
        !            91:                snprintf ( buf, sizeof ( buf ), "tftp://%s/%s",
        !            92:                           inet_ntoa ( next_server ), filename );
        !            93:                uri = parse_uri ( buf );
        !            94:                if ( ! uri )
        !            95:                        return NULL;
        !            96:        }
        !            97: 
        !            98:        return uri;
        !            99: }
        !           100: 
        !           101: /** The "keep-san" setting */
        !           102: struct setting keep_san_setting __setting ( SETTING_SANBOOT_EXTRA ) = {
        !           103:        .name = "keep-san",
        !           104:        .description = "Preserve SAN connection",
        !           105:        .tag = DHCP_EB_KEEP_SAN,
        !           106:        .type = &setting_type_int8,
        !           107: };
        !           108: 
        !           109: /** The "skip-san-boot" setting */
        !           110: struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA ) = {
        !           111:        .name = "skip-san-boot",
        !           112:        .description = "Do not boot from SAN device",
        !           113:        .tag = DHCP_EB_SKIP_SAN_BOOT,
        !           114:        .type = &setting_type_int8,
        !           115: };
        !           116: 
        !           117: /**
        !           118:  * Boot from filename and root-path URIs
        !           119:  *
        !           120:  * @v filename         Filename
        !           121:  * @v root_path                Root path
        !           122:  * @ret rc             Return status code
        !           123:  */
        !           124: int uriboot ( struct uri *filename, struct uri *root_path ) {
        !           125:        int drive;
        !           126:        int rc;
        !           127: 
        !           128:        /* Treat empty URIs as absent */
        !           129:        if ( filename && ( ! uri_has_path ( filename ) ) )
        !           130:                filename = NULL;
        !           131:        if ( root_path && ( ! uri_is_absolute ( root_path ) ) )
        !           132:                root_path = NULL;
        !           133: 
        !           134:        /* If we have both a filename and a root path, ignore an
        !           135:         * unsupported URI scheme in the root path, since it may
        !           136:         * represent an NFS root.
        !           137:         */
        !           138:        if ( filename && root_path &&
        !           139:             ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) {
        !           140:                printf ( "Ignoring unsupported root path\n" );
        !           141:                root_path = NULL;
        !           142:        }
        !           143: 
        !           144:        /* Check that we have something to boot */
        !           145:        if ( ! ( filename || root_path ) ) {
        !           146:                rc = -ENOENT_BOOT;
        !           147:                printf ( "Nothing to boot: %s\n", strerror ( rc ) );
        !           148:                goto err_no_boot;
        !           149:        }
        !           150: 
        !           151:        /* Hook SAN device, if applicable */
        !           152:        if ( root_path ) {
        !           153:                drive = san_hook ( root_path, 0 );
        !           154:                if ( drive < 0 ) {
        !           155:                        rc = drive;
        !           156:                        printf ( "Could not open SAN device: %s\n",
        !           157:                                 strerror ( rc ) );
        !           158:                        goto err_san_hook;
        !           159:                }
        !           160:                printf ( "Registered as SAN device %#02x\n", drive );
        !           161:        } else {
        !           162:                drive = -ENODEV;
        !           163:        }
        !           164: 
        !           165:        /* Describe SAN device, if applicable */
        !           166:        if ( ( drive >= 0 ) && ( ( rc = san_describe ( drive ) ) != 0 ) ) {
        !           167:                printf ( "Could not describe SAN device %#02x: %s\n",
        !           168:                         drive, strerror ( rc ) );
        !           169:                goto err_san_describe;
        !           170:        }
        !           171: 
        !           172:        /* Allow a root-path-only boot with skip-san enabled to succeed */
        !           173:        rc = 0;
        !           174: 
        !           175:        /* Attempt filename boot if applicable */
        !           176:        if ( filename ) {
        !           177:                if ( ( rc = imgdownload ( filename, NULL, NULL,
        !           178:                                          register_and_boot_image ) ) != 0 ) {
        !           179:                        printf ( "\nCould not chain image: %s\n",
        !           180:                                 strerror ( rc ) );
        !           181:                        /* Fall through to (possibly) attempt a SAN boot
        !           182:                         * as a fallback.  If no SAN boot is attempted,
        !           183:                         * our status will become the return status.
        !           184:                         */
        !           185:                } else {
        !           186:                        /* Always print an extra newline, because we
        !           187:                         * don't know where the NBP may have left the
        !           188:                         * cursor.
        !           189:                         */
        !           190:                        printf ( "\n" );
        !           191:                }
        !           192:        }
        !           193: 
        !           194:        /* Attempt SAN boot if applicable */
        !           195:        if ( root_path ) {
        !           196:                if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) {
        !           197:                        printf ( "Booting from SAN device %#02x\n", drive );
        !           198:                        rc = san_boot ( drive );
        !           199:                        printf ( "Boot from SAN device %#02x failed: %s\n",
        !           200:                                 drive, strerror ( rc ) );
        !           201:                } else {
        !           202:                        printf ( "Skipping boot from SAN device %#02x\n",
        !           203:                                 drive );
        !           204:                        /* Avoid overwriting a possible failure status
        !           205:                         * from a filename boot.
        !           206:                         */
        !           207:                }
        !           208:        }
        !           209: 
        !           210:  err_san_describe:
        !           211:        /* Unhook SAN device, if applicable */
        !           212:        if ( drive >= 0 ) {
        !           213:                if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) {
        !           214:                        printf ( "Unregistering SAN device %#02x\n", drive );
        !           215:                        san_unhook ( drive );
        !           216:                } else {
        !           217:                        printf ( "Preserving connection to SAN device %#02x\n",
        !           218:                                 drive );
        !           219:                }
        !           220:        }
        !           221:  err_san_hook:
        !           222:  err_no_boot:
        !           223:        return rc;
        !           224: }
        !           225: 
        !           226: /**
        !           227:  * Close all open net devices
        !           228:  *
        !           229:  * Called before a fresh boot attempt in order to free up memory.  We
        !           230:  * don't just close the device immediately after the boot fails,
        !           231:  * because there may still be TCP connections in the process of
        !           232:  * closing.
        !           233:  */
        !           234: static void close_all_netdevs ( void ) {
        !           235:        struct net_device *netdev;
        !           236: 
        !           237:        for_each_netdev ( netdev ) {
        !           238:                ifclose ( netdev );
        !           239:        }
        !           240: }
        !           241: 
        !           242: /**
        !           243:  * Fetch next-server and filename settings into a URI
        !           244:  *
        !           245:  * @v settings         Settings block
        !           246:  * @ret uri            URI, or NULL on failure
        !           247:  */
        !           248: struct uri * fetch_next_server_and_filename ( struct settings *settings ) {
        !           249:        struct in_addr next_server;
        !           250:        char buf[256];
        !           251:        char *filename;
        !           252:        struct uri *uri;
        !           253: 
        !           254:        /* Fetch next-server setting */
        !           255:        fetch_ipv4_setting ( settings, &next_server_setting, &next_server );
        !           256:        if ( next_server.s_addr )
        !           257:                printf ( "Next server: %s\n", inet_ntoa ( next_server ) );
        !           258: 
        !           259:        /* Fetch filename setting */
        !           260:        fetch_string_setting ( settings, &filename_setting,
        !           261:                               buf, sizeof ( buf ) );
        !           262:        if ( buf[0] )
        !           263:                printf ( "Filename: %s\n", buf );
        !           264: 
        !           265:        /* Expand filename setting */
        !           266:        filename = expand_settings ( buf );
        !           267:        if ( ! filename )
        !           268:                return NULL;
        !           269: 
        !           270:        /* Parse next server and filename */
        !           271:        uri = parse_next_server_and_filename ( next_server, filename );
        !           272: 
        !           273:        free ( filename );
        !           274:        return uri;
        !           275: }
        !           276: 
        !           277: /**
        !           278:  * Fetch root-path setting into a URI
        !           279:  *
        !           280:  * @v settings         Settings block
        !           281:  * @ret uri            URI, or NULL on failure
        !           282:  */
        !           283: static struct uri * fetch_root_path ( struct settings *settings ) {
        !           284:        char buf[256];
        !           285:        char *root_path;
        !           286:        struct uri *uri;
        !           287: 
        !           288:        /* Fetch root-path setting */
        !           289:        fetch_string_setting ( settings, &root_path_setting,
        !           290:                               buf, sizeof ( buf ) );
        !           291:        if ( buf[0] )
        !           292:                printf ( "Root path: %s\n", buf );
        !           293: 
        !           294:        /* Expand filename setting */
        !           295:        root_path = expand_settings ( buf );
        !           296:        if ( ! root_path )
        !           297:                return NULL;
        !           298: 
        !           299:        /* Parse root path */
        !           300:        uri = parse_uri ( root_path );
        !           301: 
        !           302:        free ( root_path );
        !           303:        return uri;
        !           304: }
        !           305: 
        !           306: /**
        !           307:  * Check whether or not we have a usable PXE menu
        !           308:  *
        !           309:  * @ret have_menu      A usable PXE menu is present
        !           310:  */
        !           311: static int have_pxe_menu ( void ) {
        !           312:        struct setting vendor_class_id_setting
        !           313:                = { .tag = DHCP_VENDOR_CLASS_ID };
        !           314:        struct setting pxe_discovery_control_setting
        !           315:                = { .tag = DHCP_PXE_DISCOVERY_CONTROL };
        !           316:        struct setting pxe_boot_menu_setting
        !           317:                = { .tag = DHCP_PXE_BOOT_MENU };
        !           318:        char buf[256];
        !           319:        unsigned int pxe_discovery_control;
        !           320: 
        !           321:        fetch_string_setting ( NULL, &vendor_class_id_setting,
        !           322:                               buf, sizeof ( buf ) );
        !           323:        pxe_discovery_control =
        !           324:                fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
        !           325: 
        !           326:        return ( ( strcmp ( buf, "PXEClient" ) == 0 ) &&
        !           327:                 setting_exists ( NULL, &pxe_boot_menu_setting ) &&
        !           328:                 ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) &&
        !           329:                       setting_exists ( NULL, &filename_setting ) ) ) );
        !           330: }
        !           331: 
        !           332: /**
        !           333:  * Boot from a network device
        !           334:  *
        !           335:  * @v netdev           Network device
        !           336:  * @ret rc             Return status code
        !           337:  */
        !           338: int netboot ( struct net_device *netdev ) {
        !           339:        struct uri *filename;
        !           340:        struct uri *root_path;
        !           341:        int rc;
        !           342: 
        !           343:        /* Close all other network devices */
        !           344:        close_all_netdevs();
        !           345: 
        !           346:        /* Open device and display device status */
        !           347:        if ( ( rc = ifopen ( netdev ) ) != 0 )
        !           348:                goto err_ifopen;
        !           349:        ifstat ( netdev );
        !           350: 
        !           351:        /* Configure device via DHCP */
        !           352:        if ( ( rc = dhcp ( netdev ) ) != 0 )
        !           353:                goto err_dhcp;
        !           354:        route();
        !           355: 
        !           356:        /* Try PXE menu boot, if applicable */
        !           357:        if ( have_pxe_menu() ) {
        !           358:                printf ( "Booting from PXE menu\n" );
        !           359:                rc = pxe_menu_boot ( netdev );
        !           360:                goto err_pxe_menu_boot;
        !           361:        }
        !           362: 
        !           363:        /* Fetch next server, filename and root path */
        !           364:        filename = fetch_next_server_and_filename ( NULL );
        !           365:        if ( ! filename )
        !           366:                goto err_filename;
        !           367:        root_path = fetch_root_path ( NULL );
        !           368:        if ( ! root_path )
        !           369:                goto err_root_path;
        !           370: 
        !           371:        /* Boot using next server, filename and root path */
        !           372:        if ( ( rc = uriboot ( filename, root_path ) ) != 0 )
        !           373:                goto err_uriboot;
        !           374: 
        !           375:  err_uriboot:
        !           376:        uri_put ( root_path );
        !           377:  err_root_path:
        !           378:        uri_put ( filename );
        !           379:  err_filename:
        !           380:  err_pxe_menu_boot:
        !           381:  err_dhcp:
        !           382:  err_ifopen:
        !           383:        return rc;
        !           384: }
        !           385: 
        !           386: /**
        !           387:  * Boot the system
        !           388:  */
        !           389: int autoboot ( void ) {
        !           390:        struct net_device *boot_netdev;
        !           391:        struct net_device *netdev;
        !           392:        int rc = -ENODEV;
        !           393: 
        !           394:        /* If we have an identifable boot device, try that first */
        !           395:        if ( ( boot_netdev = find_boot_netdev() ) )
        !           396:                rc = netboot ( boot_netdev );
        !           397: 
        !           398:        /* If that fails, try booting from any of the other devices */
        !           399:        for_each_netdev ( netdev ) {
        !           400:                if ( netdev == boot_netdev )
        !           401:                        continue;
        !           402:                rc = netboot ( netdev );
        !           403:        }
        !           404: 
        !           405:        printf ( "No more network devices\n" );
        !           406:        return rc;
        !           407: }

unix.superglobalmegacorp.com

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