Initial import from FreeBSD RELENG_4:
[games.git] / sys / boot / common / boot.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/boot/common/boot.c,v 1.15.2.5 2000/12/28 13:12:33 ps Exp $
27  */
28
29 /*
30  * Loading modules, booting the system
31  */
32
33 #include <stand.h>
34 #include <string.h>
35
36 #include "bootstrap.h"
37
38 static char     *getbootfile(int try);
39
40 /* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */
41 static const char *default_bootfiles = "kernel;kernel.old";
42
43 static int autoboot_tried;
44
45 /*
46  * The user wants us to boot.
47  */
48 COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot);
49
50 static int
51 command_boot(int argc, char *argv[])
52 {
53     struct loaded_module        *km;
54     char                        *cp;
55     int                         try;
56     
57     /*
58      * See if the user has specified an explicit kernel to boot.
59      */
60     if ((argc > 1) && (argv[1][0] != '-')) {
61         
62         /* XXX maybe we should discard everything and start again? */
63         if (mod_findmodule(NULL, NULL) != NULL) {
64             sprintf(command_errbuf, "can't boot '%s', kernel module already loaded", argv[1]);
65             return(CMD_ERROR);
66         }
67         
68         /* find/load the kernel module */
69         if (mod_load(argv[1], argc - 2, argv + 2) != 0)
70             return(CMD_ERROR);
71         /* we have consumed all arguments */
72         argc = 1;
73     }
74
75     /*
76      * See if there is a kernel module already loaded
77      */
78     if (mod_findmodule(NULL, NULL) == NULL) {
79         for (try = 0; (cp = getbootfile(try)) != NULL; try++) {
80             if (mod_load(cp, argc - 1, argv + 1) != 0) {
81                 printf("can't load '%s'\n", cp);
82             } else {
83                 /* we have consumed all arguments */
84                 argc = 1;
85                 break;
86             }
87         }
88     }
89
90     /*
91      * Loaded anything yet?
92      */
93     if ((km = mod_findmodule(NULL, NULL)) == NULL) {
94         command_errmsg = "no bootable kernel";
95         return(CMD_ERROR);
96     }
97
98     /*
99      * If we were given arguments, discard any previous.
100      * XXX should we merge arguments?  Hard to DWIM.
101      */
102     if (argc > 1) {
103         if (km->m_args != NULL) 
104             free(km->m_args);
105         km->m_args = unargv(argc - 1, argv + 1);
106     }
107
108     /* Hook for platform-specific autoloading of modules */
109     if (archsw.arch_autoload() != 0)
110         return(CMD_ERROR);
111
112     /* Call the exec handler from the loader matching the kernel */
113     module_formats[km->m_loader]->l_exec(km);
114     return(CMD_ERROR);
115 }
116
117
118 /*
119  * Autoboot after a delay
120  */
121
122 COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot);
123
124 static int
125 command_autoboot(int argc, char *argv[])
126 {
127     int         howlong;
128     char        *cp, *prompt;
129
130     prompt = NULL;
131     howlong = -1;
132     switch(argc) {
133     case 3:
134         prompt = argv[2];
135         /* FALLTHROUGH */
136     case 2:
137         howlong = strtol(argv[1], &cp, 0);
138         if (*cp != 0) {
139             sprintf(command_errbuf, "bad delay '%s'", argv[1]);
140             return(CMD_ERROR);
141         }
142         /* FALLTHROUGH */
143     case 1:
144         return(autoboot(howlong, prompt));
145     }
146         
147     command_errmsg = "too many arguments";
148     return(CMD_ERROR);
149 }
150
151 /*
152  * Called before we go interactive.  If we think we can autoboot, and
153  * we haven't tried already, try now.
154  */
155 void
156 autoboot_maybe()
157 {
158     char        *cp;
159     
160     cp = getenv("autoboot_delay");
161     if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO")))
162         autoboot(-1, NULL);             /* try to boot automatically */
163 }
164
165 int
166 autoboot(int timeout, char *prompt)
167 {
168     time_t      when, otime, ntime;
169     int         c, yes;
170     char        *argv[2], *cp, *ep;
171
172     autoboot_tried = 1;
173
174     if (timeout == -1) {
175         /* try to get a delay from the environment */
176         if ((cp = getenv("autoboot_delay"))) {
177             timeout = strtol(cp, &ep, 0);
178             if (cp == ep)
179                 timeout = -1;
180         }
181     }
182     if (timeout == -1)          /* all else fails */
183         timeout = 10;
184
185     otime = time(NULL);
186     when = otime + timeout;     /* when to boot */
187     yes = 0;
188
189     /* XXX could try to work out what we might boot */
190     printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt);
191
192     for (;;) {
193         if (ischar()) {
194             c = getchar();
195             if ((c == '\r') || (c == '\n'))
196                 yes = 1;
197             break;
198         }
199         ntime = time(NULL);
200         if (ntime >= when) {
201             yes = 1;
202             break;
203         }
204         if (ntime != otime) {
205             printf("\rBooting [%s] in %d second%s... ",
206                         getbootfile(0), (int)(when - ntime),
207                         (when-ntime)==1?"":"s");
208             otime = ntime;
209         }
210     }
211     if (yes)
212         printf("\rBooting [%s]...               ", getbootfile(0));
213     putchar('\n');
214     if (yes) {
215         argv[0] = "boot";
216         argv[1] = NULL;
217         return(command_boot(1, argv));
218     }
219     return(CMD_OK);
220 }
221
222 /*
223  * Scrounge for the name of the (try)'th file we will try to boot.
224  */
225 static char *
226 getbootfile(int try) 
227 {
228     static char *name = NULL;
229     char        *spec, *ep;
230     size_t      len;
231     
232     /* we use dynamic storage */
233     if (name != NULL) {
234         free(name);
235         name = NULL;
236     }
237     
238     /* 
239      * Try $bootfile, then try our builtin default
240      */
241     if ((spec = getenv("bootfile")) == NULL)
242         spec = default_bootfiles;
243
244     while ((try > 0) && (spec != NULL)) {
245         spec = strchr(spec, ';');
246         if (spec)
247             spec++;     /* skip over the leading ';' */
248         try--;
249     }
250     if (spec != NULL) {
251         if ((ep = strchr(spec, ';')) != NULL) {
252             len = ep - spec;
253         } else {
254             len = strlen(spec);
255         }
256         name = malloc(len + 1);
257         strncpy(name, spec, len);
258         name[len] = 0;
259     }
260     if (name && name[0] == 0) {
261         free(name);
262         name = NULL;
263     }
264     return(name);
265 }
266
267 /*
268  * Try to find the /etc/fstab file on the filesystem (rootdev),
269  * which should be be the root filesystem, and parse it to find 
270  * out what the kernel ought to think the root filesystem is.
271  *
272  * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
273  * so that the kernel can tell both which VFS and which node to use
274  * to mount the device.  If this variable's already set, don't
275  * overwrite it.
276  */
277 int
278 getrootmount(char *rootdev)
279 {
280     char        lbuf[128], *cp, *ep, *dev, *fstyp;
281     int         fd, error;
282
283     if (getenv("vfs.root.mountfrom") != NULL)
284         return(0);
285
286     sprintf(lbuf, "%s/etc/fstab", rootdev);
287     if ((fd = open(lbuf, O_RDONLY)) < 0)
288         return(1);
289
290     /* loop reading lines from /etc/fstab    What was that about sscanf again? */
291     error = 1;
292     while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) {
293         if ((lbuf[0] == 0) || (lbuf[0] == '#'))
294             continue;
295         
296         /* skip device name */
297         for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
298             ;
299         if (*cp == 0)           /* misformatted */
300             continue;
301         /* delimit and save */
302         *cp++ = 0;
303         dev = strdup(lbuf);
304     
305         /* skip whitespace up to mountpoint */
306         while ((*cp != 0) && isspace(*cp))
307             cp++;
308         /* must have /<space> to be root */
309         if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1)))
310             continue;
311         /* skip whitespace up to fstype */
312         cp += 2;
313         while ((*cp != 0) && isspace(*cp))
314             cp++;
315         if (*cp == 0)           /* misformatted */
316             continue;
317         /* skip text to end of fstype and delimit */
318         ep = cp;
319         while ((*cp != 0) && !isspace(*cp))
320             cp++;
321         *cp = 0;
322         fstyp = strdup(ep);
323
324         /* build the final result and save it */
325         sprintf(lbuf, "%s:%s", fstyp, dev);
326         free(dev);
327         free(fstyp);
328         setenv("vfs.root.mountfrom", lbuf, 0);
329         error = 0;
330         break;
331     }
332     close(fd);
333     return(error);
334 }