[autoboot] Retain initial-slash (if present) when constructing TFTP URIs
[people/balajirrao/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 <gpxe/uri.h>
28 #include <usr/ifmgmt.h>
29 #include <usr/route.h>
30 #include <usr/dhcpmgmt.h>
31 #include <usr/imgmgmt.h>
32 #include <usr/iscsiboot.h>
33 #include <usr/aoeboot.h>
34 #include <usr/autoboot.h>
35
36 /** @file
37  *
38  * Automatic booting
39  *
40  */
41
42 /** Time to wait for link-up */
43 #define LINK_WAIT_MS 15000
44
45 /** Shutdown flags for exit */
46 int shutdown_exit_flags = 0;
47
48 /**
49  * Identify the boot network device
50  *
51  * @ret netdev          Boot network device
52  */
53 static struct net_device * find_boot_netdev ( void ) {
54         return NULL;
55 }
56
57 /**
58  * Boot embedded image
59  *
60  * @ret rc              Return status code
61  */
62 static int boot_embedded_image ( void ) {
63         struct image *image;
64         int rc;
65
66         image = embedded_image();
67         if ( !image )
68                 return ENOENT;
69
70         if ( ( rc = imgload ( image ) ) != 0 ) {
71                 printf ( "Could not load embedded image: %s\n",
72                          strerror ( rc ) );
73         } else if ( ( rc = imgexec ( image ) ) != 0 ) {
74                 printf ( "Could not boot embedded image: %s\n",
75                          strerror ( rc ) );
76         }
77         image_put ( image );
78         return rc;
79 }
80
81 /**
82  * Boot using next-server and filename
83  *
84  * @v filename          Boot filename
85  * @ret rc              Return status code
86  */
87 static int boot_next_server_and_filename ( struct in_addr next_server,
88                                            const char *filename ) {
89         struct uri *uri;
90         struct image *image;
91         char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ + strlen(filename) + 1 ];
92         int filename_is_absolute;
93         int rc;
94
95         /* Construct URI */
96         uri = parse_uri ( filename );
97         if ( ! uri ) {
98                 printf ( "Out of memory\n" );
99                 return -ENOMEM;
100         }
101         filename_is_absolute = uri_is_absolute ( uri );
102         uri_put ( uri );
103         if ( ! filename_is_absolute ) {
104                 /* Construct a tftp:// URI for the filename.  We can't
105                  * just rely on the current working URI, because the
106                  * relative URI resolution will remove the distinction
107                  * between filenames with and without initial slashes,
108                  * which is significant for TFTP.
109                  */
110                 snprintf ( buf, sizeof ( buf ), "tftp://%s/%s",
111                            inet_ntoa ( next_server ), filename );
112                 filename = buf;
113         }
114
115         image = alloc_image();
116         if ( ! image ) {
117                 printf ( "Out of memory\n" );
118                 return -ENOMEM;
119         }
120         if ( ( rc = imgfetch ( image, filename,
121                                register_and_autoload_image ) ) != 0 ) {
122                 printf ( "Could not load %s: %s\n",
123                          filename, strerror ( rc ) );
124                 goto done;
125         }
126         if ( ( rc = imgexec ( image ) ) != 0 ) {
127                 printf ( "Could not boot %s: %s\n",
128                          filename, strerror ( rc ) );
129                 goto done;
130         }
131
132  done:
133         image_put ( image );
134         return rc;
135 }
136
137 /**
138  * Boot using root path
139  *
140  * @v root_path         Root path
141  * @ret rc              Return status code
142  */
143 int boot_root_path ( const char *root_path ) {
144
145         /* Quick hack */
146         if ( strncmp ( root_path, "iscsi:", 6 ) == 0 ) {
147                 return iscsiboot ( root_path );
148         } else if ( strncmp ( root_path, "aoe:", 4 ) == 0 ) {
149                 return aoeboot ( root_path );
150         }
151
152         return -ENOTSUP;
153 }
154
155 /**
156  * Boot from a network device
157  *
158  * @v netdev            Network device
159  * @ret rc              Return status code
160  */
161 static int netboot ( struct net_device *netdev ) {
162         char buf[256];
163         struct in_addr next_server;
164         int rc;
165
166         /* Open device and display device status */
167         if ( ( rc = ifopen ( netdev ) ) != 0 )
168                 return rc;
169         ifstat ( netdev );
170
171         /* Wait for link-up */
172         printf ( "Waiting for link-up on %s...", netdev->name );
173         if ( ( rc = iflinkwait ( netdev, LINK_WAIT_MS ) ) != 0 ) {
174                 printf ( " no link detected\n" );
175                 return rc;
176         }
177         printf ( " ok\n" );
178
179         /* Configure device via DHCP */
180         if ( ( rc = dhcp ( netdev ) ) != 0 )
181                 return rc;
182         route();
183
184         /* Try to boot an embedded image if we have one */
185         rc = boot_embedded_image ();
186         if ( rc != ENOENT )
187                 return rc;
188
189         /* Try to download and boot whatever we are given as a filename */
190         fetch_ipv4_setting ( NULL, &next_server_setting, &next_server );
191         fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) );
192         if ( buf[0] ) {
193                 printf ( "Booting from filename \"%s\"\n", buf );
194                 return boot_next_server_and_filename ( next_server, buf );
195         }
196         
197         /* No filename; try the root path */
198         fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) );
199         if ( buf[0] ) {
200                 printf ( "Booting from root path \"%s\"\n", buf );
201                 return boot_root_path ( buf );
202         }
203
204         printf ( "No filename or root path specified\n" );
205         return -ENOENT;
206 }
207
208 /**
209  * Close all open net devices
210  *
211  * Called before a fresh boot attempt in order to free up memory.  We
212  * don't just close the device immediately after the boot fails,
213  * because there may still be TCP connections in the process of
214  * closing.
215  */
216 static void close_all_netdevs ( void ) {
217         struct net_device *netdev;
218
219         for_each_netdev ( netdev ) {
220                 ifclose ( netdev );
221         }
222 }
223
224 /**
225  * Boot the system
226  */
227 void autoboot ( void ) {
228         struct net_device *boot_netdev;
229         struct net_device *netdev;
230
231         /* If we have an identifable boot device, try that first */
232         close_all_netdevs();
233         if ( ( boot_netdev = find_boot_netdev() ) )
234                 netboot ( boot_netdev );
235
236         /* If that fails, try booting from any of the other devices */
237         for_each_netdev ( netdev ) {
238                 if ( netdev == boot_netdev )
239                         continue;
240                 close_all_netdevs();
241                 netboot ( netdev );
242         }
243
244         printf ( "No more network devices\n" );
245 }