[Settings] Convert code in src/usr to use settings API.
[people/dverkamp/gpxe.git] / src / usr / autoboot.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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 #include <string.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <gpxe/netdevice.h>
23 #include <gpxe/dhcp.h>
24 #include <gpxe/settings.h>
25 #include <gpxe/image.h>
26 #include <gpxe/embedded.h>
27 #include <usr/ifmgmt.h>
28 #include <usr/route.h>
29 #include <usr/dhcpmgmt.h>
30 #include <usr/imgmgmt.h>
31 #include <usr/iscsiboot.h>
32 #include <usr/aoeboot.h>
33 #include <usr/autoboot.h>
34
35 /** @file
36  *
37  * Automatic booting
38  *
39  */
40
41 /**
42  * Identify the boot network device
43  *
44  * @ret netdev          Boot network device
45  */
46 static struct net_device * find_boot_netdev ( void ) {
47         return NULL;
48 }
49
50 /**
51  * Boot embedded image
52  *
53  * @ret rc              Return status code
54  */
55 static int boot_embedded_image ( void ) {
56         struct image *image;
57         int rc;
58
59         image = embedded_image();
60         if ( !image )
61                 return ENOENT;
62
63         if ( ( rc = imgload ( image ) ) != 0 ) {
64                 printf ( "Could not load embedded image: %s\n",
65                          strerror ( rc ) );
66         } else if ( ( rc = imgexec ( image ) ) != 0 ) {
67                 printf ( "Could not boot embedded image: %s\n",
68                          strerror ( rc ) );
69         }
70         image_put ( image );
71         return rc;
72 }
73
74 /**
75  * Boot using filename
76  *
77  * @v filename          Boot filename
78  * @ret rc              Return status code
79  */
80 static int boot_filename ( const char *filename ) {
81         struct image *image;
82         int rc;
83
84         image = alloc_image();
85         if ( ! image ) {
86                 printf ( "Out of memory\n" );
87                 return -ENOMEM;
88         }
89         if ( ( rc = imgfetch ( image, filename,
90                                register_and_autoload_image ) ) != 0 ) {
91                 printf ( "Could not load %s: %s\n",
92                          filename, strerror ( rc ) );
93                 goto done;
94         }
95         if ( ( rc = imgexec ( image ) ) != 0 ) {
96                 printf ( "Could not boot %s: %s\n",
97                          filename, strerror ( rc ) );
98                 goto done;
99         }
100
101  done:
102         image_put ( image );
103         return rc;
104 }
105
106 /**
107  * Boot using root path
108  *
109  * @v root_path         Root path
110  * @ret rc              Return status code
111  */
112 int boot_root_path ( const char *root_path ) {
113
114         /* Quick hack */
115         if ( strncmp ( root_path, "iscsi:", 6 ) == 0 ) {
116                 return iscsiboot ( root_path );
117         } else if ( strncmp ( root_path, "aoe:", 4 ) == 0 ) {
118                 return aoeboot ( root_path );
119         }
120
121         return -ENOTSUP;
122 }
123
124 /**
125  * Boot from a network device
126  *
127  * @v netdev            Network device
128  * @ret rc              Return status code
129  */
130 static int netboot ( struct net_device *netdev ) {
131         char buf[256];
132         int rc;
133
134         /* Open device and display device status */
135         if ( ( rc = ifopen ( netdev ) ) != 0 )
136                 return rc;
137         ifstat ( netdev );
138
139         /* Configure device via DHCP */
140         if ( ( rc = dhcp ( netdev ) ) != 0 )
141                 return rc;
142         route();
143
144         /* Try to boot an embedded image if we have one */
145         rc = boot_embedded_image ();
146         if ( rc != ENOENT )
147                 return rc;
148
149         /* Try to download and boot whatever we are given as a filename */
150         fetch_string_setting ( NULL, DHCP_BOOTFILE_NAME, buf, sizeof ( buf ) );
151         if ( buf[0] ) {
152                 printf ( "Booting from filename \"%s\"\n", buf );
153                 return boot_filename ( buf );
154         }
155         
156         /* No filename; try the root path */
157         fetch_string_setting ( NULL, DHCP_ROOT_PATH, buf, sizeof ( buf ) );
158         if ( buf[0] ) {
159                 printf ( "Booting from root path \"%s\"\n", buf );
160                 return boot_root_path ( buf );
161         }
162
163         printf ( "No filename or root path specified\n" );
164         return -ENOENT;
165 }
166
167 /**
168  * Close all open net devices
169  *
170  * Called before a fresh boot attempt in order to free up memory.  We
171  * don't just close the device immediately after the boot fails,
172  * because there may still be TCP connections in the process of
173  * closing.
174  */
175 static void close_all_netdevs ( void ) {
176         struct net_device *netdev;
177
178         for_each_netdev ( netdev ) {
179                 ifclose ( netdev );
180         }
181 }
182
183 /**
184  * Boot the system
185  */
186 void autoboot ( void ) {
187         struct net_device *boot_netdev;
188         struct net_device *netdev;
189
190         /* If we have an identifable boot device, try that first */
191         close_all_netdevs();
192         if ( ( boot_netdev = find_boot_netdev() ) )
193                 netboot ( boot_netdev );
194
195         /* If that fails, try booting from any of the other devices */
196         for_each_netdev ( netdev ) {
197                 if ( netdev == boot_netdev )
198                         continue;
199                 close_all_netdevs();
200                 netboot ( netdev );
201         }
202
203         printf ( "No more network devices\n" );
204 }