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