]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/boot.c
MFC r334432:
[FreeBSD/FreeBSD.git] / stand / 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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 /*
31  * Loading modules, booting the system
32  */
33
34 #include <stand.h>
35 #include <string.h>
36
37 #include "bootstrap.h"
38
39 static int      autoboot(int timeout, char *prompt);
40 static char     *getbootfile(int try);
41 static int      loadakernel(int try, int argc, char* argv[]);
42
43 /* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */
44 static const char *default_bootfiles = "kernel";
45
46 static int autoboot_tried;
47
48 /*
49  * The user wants us to boot.
50  */
51 COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot);
52
53 static int
54 command_boot(int argc, char *argv[])
55 {
56         struct preloaded_file   *fp;
57
58         /*
59          * See if the user has specified an explicit kernel to boot.
60          */
61         if ((argc > 1) && (argv[1][0] != '-')) {
62
63                 /* XXX maybe we should discard everything and start again? */
64                 if (file_findfile(NULL, NULL) != NULL) {
65                         snprintf(command_errbuf, sizeof(command_errbuf),
66                             "can't boot '%s', kernel module already loaded", argv[1]);
67                         return(CMD_ERROR);
68                 }
69
70                 /* find/load the kernel module */
71                 if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0)
72                         return(CMD_ERROR);
73                 /* we have consumed all arguments */
74                 argc = 1;
75         }
76
77         /*
78          * See if there is a kernel module already loaded
79          */
80         if (file_findfile(NULL, NULL) == NULL)
81                 if (loadakernel(0, argc - 1, argv + 1))
82                         /* we have consumed all arguments */
83                         argc = 1;
84
85         /*
86          * Loaded anything yet?
87          */
88         if ((fp = file_findfile(NULL, NULL)) == NULL) {
89                 command_errmsg = "no bootable kernel";
90                 return(CMD_ERROR);
91         }
92
93         /*
94          * If we were given arguments, discard any previous.
95          * XXX should we merge arguments?  Hard to DWIM.
96          */
97         if (argc > 1) {
98                 if (fp->f_args != NULL)
99                         free(fp->f_args);
100                 fp->f_args = unargv(argc - 1, argv + 1);
101         }
102
103         /* Hook for platform-specific autoloading of modules */
104         if (archsw.arch_autoload() != 0)
105                 return(CMD_ERROR);
106
107         /* Call the exec handler from the loader matching the kernel */
108         file_formats[fp->f_loader]->l_exec(fp);
109         return(CMD_ERROR);
110 }
111
112
113 /*
114  * Autoboot after a delay
115  */
116
117 COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot);
118
119 static int
120 command_autoboot(int argc, char *argv[])
121 {
122         int             howlong;
123         char    *cp, *prompt;
124
125         prompt = NULL;
126         howlong = -1;
127         switch(argc) {
128         case 3:
129                 prompt = argv[2];
130                 /* FALLTHROUGH */
131         case 2:
132                 howlong = strtol(argv[1], &cp, 0);
133                 if (*cp != 0) {
134                         snprintf(command_errbuf, sizeof(command_errbuf),
135                             "bad delay '%s'", argv[1]);
136                         return(CMD_ERROR);
137                 }
138                 /* FALLTHROUGH */
139         case 1:
140                 return(autoboot(howlong, prompt));
141         }
142
143         command_errmsg = "too many arguments";
144         return(CMD_ERROR);
145 }
146
147 /*
148  * Called before we go interactive.  If we think we can autoboot, and
149  * we haven't tried already, try now.
150  */
151 void
152 autoboot_maybe()
153 {
154         char    *cp;
155
156         cp = getenv("autoboot_delay");
157         if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO")))
158                 autoboot(-1, NULL);             /* try to boot automatically */
159 }
160
161 static int
162 autoboot(int timeout, char *prompt)
163 {
164         time_t  when, otime, ntime;
165         int             c, yes;
166         char    *argv[2], *cp, *ep;
167         char    *kernelname;
168 #ifdef BOOT_PROMPT_123
169         const char      *seq = "123", *p = seq;
170 #endif
171
172         autoboot_tried = 1;
173
174         if (timeout == -1) {
175                 timeout = 10;
176                 /* try to get a delay from the environment */
177                 if ((cp = getenv("autoboot_delay"))) {
178                         timeout = strtol(cp, &ep, 0);
179                         if (cp == ep)
180                                 timeout = 10;           /* Unparseable? Set default! */
181                 }
182         }
183
184         kernelname = getenv("kernelname");
185         if (kernelname == NULL) {
186                 argv[0] = NULL;
187                 loadakernel(0, 0, argv);
188                 kernelname = getenv("kernelname");
189                 if (kernelname == NULL) {
190                         command_errmsg = "no valid kernel found";
191                         return(CMD_ERROR);
192                 }
193         }
194
195         if (timeout >= 0) {
196                 otime = time(NULL);
197                 when = otime + timeout; /* when to boot */
198
199                 yes = 0;
200
201 #ifdef BOOT_PROMPT_123
202                 printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or "
203                     "1 2 3 sequence for command prompt." : prompt);
204 #else
205                 printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt);
206 #endif
207
208                 for (;;) {
209                         if (ischar()) {
210                                 c = getchar();
211 #ifdef BOOT_PROMPT_123
212                                 if ((c == '\r') || (c == '\n')) {
213                                         yes = 1;
214                                         break;
215                                 } else if (c != *p++)
216                                         p = seq;
217                                 if (*p == 0)
218                                         break;
219 #else
220                                 if ((c == '\r') || (c == '\n'))
221                                         yes = 1;
222                                 break;
223 #endif
224                         }
225                         ntime = time(NULL);
226                         if (ntime >= when) {
227                                 yes = 1;
228                                 break;
229                         }
230
231                         if (ntime != otime) {
232                                 printf("\rBooting [%s] in %d second%s... ",
233                                     kernelname, (int)(when - ntime),
234                                     (when-ntime)==1?"":"s");
235                                 otime = ntime;
236                         }
237                 }
238         } else {
239                 yes = 1;
240         }
241
242         if (yes)
243                 printf("\rBooting [%s]...               ", kernelname);
244         putchar('\n');
245         if (yes) {
246                 argv[0] = "boot";
247                 argv[1] = NULL;
248                 return(command_boot(1, argv));
249         }
250         return(CMD_OK);
251 }
252
253 /*
254  * Scrounge for the name of the (try)'th file we will try to boot.
255  */
256 static char *
257 getbootfile(int try)
258 {
259         static char *name = NULL;
260         const char      *spec, *ep;
261         size_t  len;
262
263         /* we use dynamic storage */
264         if (name != NULL) {
265                 free(name);
266                 name = NULL;
267         }
268
269         /*
270          * Try $bootfile, then try our builtin default
271          */
272         if ((spec = getenv("bootfile")) == NULL)
273                 spec = default_bootfiles;
274
275         while ((try > 0) && (spec != NULL)) {
276                 spec = strchr(spec, ';');
277                 if (spec)
278                         spec++; /* skip over the leading ';' */
279                 try--;
280         }
281         if (spec != NULL) {
282                 if ((ep = strchr(spec, ';')) != NULL) {
283                         len = ep - spec;
284                 } else {
285                         len = strlen(spec);
286                 }
287                 name = malloc(len + 1);
288                 strncpy(name, spec, len);
289                 name[len] = 0;
290         }
291         if (name && name[0] == 0) {
292                 free(name);
293                 name = NULL;
294         }
295         return(name);
296 }
297
298 /*
299  * Try to find the /etc/fstab file on the filesystem (rootdev),
300  * which should be be the root filesystem, and parse it to find
301  * out what the kernel ought to think the root filesystem is.
302  *
303  * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
304  * so that the kernel can tell both which VFS and which node to use
305  * to mount the device.  If this variable's already set, don't
306  * overwrite it.
307  */
308 int
309 getrootmount(char *rootdev)
310 {
311         char    lbuf[128], *cp, *ep, *dev, *fstyp, *options;
312         int             fd, error;
313
314         if (getenv("vfs.root.mountfrom") != NULL)
315                 return(0);
316
317         error = 1;
318         sprintf(lbuf, "%s/etc/fstab", rootdev);
319         if ((fd = open(lbuf, O_RDONLY)) < 0)
320                 goto notfound;
321
322         /* loop reading lines from /etc/fstab    What was that about sscanf again? */
323         fstyp = NULL;
324         dev = NULL;
325         while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) {
326                 if ((lbuf[0] == 0) || (lbuf[0] == '#'))
327                         continue;
328
329                 /* skip device name */
330                 for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
331                         ;
332                 if (*cp == 0)           /* misformatted */
333                         continue;
334                 /* delimit and save */
335                 *cp++ = 0;
336                 free(dev);
337                 dev = strdup(lbuf);
338
339                 /* skip whitespace up to mountpoint */
340                 while ((*cp != 0) && isspace(*cp))
341                         cp++;
342                 /* must have /<space> to be root */
343                 if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1)))
344                         continue;
345                 /* skip whitespace up to fstype */
346                 cp += 2;
347                 while ((*cp != 0) && isspace(*cp))
348                         cp++;
349                 if (*cp == 0)           /* misformatted */
350                         continue;
351                 /* skip text to end of fstype and delimit */
352                 ep = cp;
353                 while ((*cp != 0) && !isspace(*cp))
354                         cp++;
355                 *cp = 0;
356                 free(fstyp);
357                 fstyp = strdup(ep);
358
359                 /* skip whitespace up to mount options */
360                 cp += 1;
361                 while ((*cp != 0) && isspace(*cp))
362                         cp++;
363                 if (*cp == 0)           /* misformatted */
364                         continue;
365                 /* skip text to end of mount options and delimit */
366                 ep = cp;
367                 while ((*cp != 0) && !isspace(*cp))
368                         cp++;
369                 *cp = 0;
370                 options = strdup(ep);
371                 /* Build the <fstype>:<device> and save it in vfs.root.mountfrom */
372                 sprintf(lbuf, "%s:%s", fstyp, dev);
373                 setenv("vfs.root.mountfrom", lbuf, 0);
374
375                 /* Don't override vfs.root.mountfrom.options if it is already set */
376                 if (getenv("vfs.root.mountfrom.options") == NULL) {
377                         /* save mount options */
378                         setenv("vfs.root.mountfrom.options", options, 0);
379                 }
380                 free(options);
381                 error = 0;
382                 break;
383         }
384         close(fd);
385         free(dev);
386         free(fstyp);
387
388 notfound:
389         if (error) {
390                 const char *currdev;
391
392                 currdev = getenv("currdev");
393                 if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) {
394                         cp = strdup(currdev);
395                         cp[strlen(cp) - 1] = '\0';
396                         setenv("vfs.root.mountfrom", cp, 0);
397                         error = 0;
398                         free(cp);
399                 }
400         }
401
402         return(error);
403 }
404
405 static int
406 loadakernel(int try, int argc, char* argv[])
407 {
408         char *cp;
409
410         for (try = 0; (cp = getbootfile(try)) != NULL; try++)
411                 if (mod_loadkld(cp, argc - 1, argv + 1) != 0)
412                         printf("can't load '%s'\n", cp);
413                 else
414                         return 1;
415         return 0;
416 }