boot system and buildkernel - Additional work
[dragonfly.git] / sys / boot / dloader / cmds.c
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <stand.h>
36 #include <string.h>
37 #include "bootstrap.h"
38 #include "dloader.h"
39
40 static void menu_display(void);
41 static int menu_execute(int);
42
43 /*
44  * This is called from common and must reference files to bring
45  * library modules into common during linking.
46  */
47 void
48 dloader_init_cmds(void)
49 {
50 }
51
52 /*
53  * This intercepts lines of the form 'a=b'
54  */
55 COMMAND_SET(local, "local", "List local variables", command_local);
56 COMMAND_SET(lunset, "lunset", "Unset local variables", command_lunset);
57 COMMAND_SET(lunsetif, "lunsetif", "Unset local if envvar set to 1 or YES", command_lunsetif);
58 COMMAND_SET(loadall, "loadall", "Load kernel + modules", command_loadall);
59 COMMAND_SET(menuclear, "menuclear", "Clear all menus", command_menuclear);
60 COMMAND_SET(menuitem, "menuitem", "Add menu bullet", command_menuitem);
61 COMMAND_SET(menuadd, "menuadd", "Add script line for bullet", command_menuadd);
62 COMMAND_SET(menu, "menu", "Run menu system", command_menu);
63
64 static int curitem;
65 static int curadd;
66
67 /*
68  * Set local variable.  Sniff "module_path"
69  *
70  * format a=b (one argument) - in av[0]
71  */
72 static int
73 command_local(int ac, char **av)
74 {
75         char *name;
76         char *data;
77         dvar_t dvar;
78         int i;
79         int j;
80
81         /*
82          * local command executed directly.
83          */
84         if (strcmp(av[0], "local") == 0) {
85                 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
86                         for (j = 1; j < ac; ++j) {
87                                 if (!strncmp(dvar->name, av[j], strlen(av[j])))
88                                         break;
89                         }
90                         if (ac > 1 && j == ac)
91                                 continue;
92
93                         printf("%s=", dvar->name);
94                         for (i = 0; i < dvar->count; ++i) {
95                                 if (i)
96                                         printf(",");
97                                 printf("\"%s\"", dvar->data[i]);
98                         }
99                         printf("\n");
100                 }
101                 return(CMD_OK);
102         }
103
104         /*
105          * local command intercept for blah=blah
106          */
107         name = av[0];
108         data = strchr(name, '=');
109         if (data == NULL) {
110                 sprintf(command_errbuf, "Bad variable syntax");
111                 return (CMD_ERROR);
112         }
113         *data++ = 0;
114
115         if (*data)
116                 dvar_set(name, &data, 1);
117         else
118                 dvar_unset(name);
119
120         /*
121          * These variable have to mirror to kenv because libstand or
122          * other consumers may have hooks into them.
123          */
124         if (strchr(name, '.') ||
125             strcmp(name, "kernelname") == 0 ||
126             strcmp(name, "module_path") == 0 ||
127             strcmp(name, "console") == 0 ||
128             strcmp(name, "currdev") == 0 ||
129             strcmp(name, "loaddev") == 0 ||
130             strcmp(name, "bootfile") == 0) {
131                 if (*data)
132                         setenv(name, data, 1);
133                 else
134                         unsetenv(name);
135         }
136         return(CMD_OK);
137 }
138
139 /*
140  * Unset local variables
141  */
142 static int
143 command_lunset(int ac, char **av)
144 {
145         int i;
146
147         for (i = 1; i < ac; ++i)
148                 dvar_unset(av[i]);
149         return(0);
150 }
151
152 static int
153 command_lunsetif(int ac, char **av)
154 {
155         char *envdata;
156
157         if (ac != 3) {
158                 sprintf(command_errbuf,
159                         "syntax error use lunsetif lname envname");
160                 return(CMD_ERROR);
161         }
162         envdata = getenv(av[2]);
163         if (strcmp(envdata, "yes") == 0 ||
164             strcmp(envdata, "YES") == 0 ||
165             strtol(envdata, NULL, 0)) {
166                 dvar_unset(av[1]);
167         }
168         return (CMD_OK);
169 }
170
171 /*
172  * Load the kernel + all modules specified with
173  */
174 static int
175 command_loadall(int ac, char **av)
176 {
177         char *argv[4];
178         dvar_t dvar;
179         int len;
180         int argc;
181         int res;
182         int tmp;
183
184         argv[0] = "unload";
185         (void)perform(1, argv);
186
187         /*
188          * Load kernel
189          */
190         argv[0] = "load";
191         argv[1] = getenv("kernelname");
192         if (argv[1] == NULL)
193                 argv[1] = strdup("kernel");
194         res = perform(2, argv);
195         free(argv[1]);
196
197         if (res != CMD_OK) {
198                 printf("Unable to load %s%s\n", DirBase, argv[1]);
199                 return(res);
200         }
201
202         /*
203          * Load modules
204          */
205         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
206                 len = strlen(dvar->name);
207                 if (len <= 5 || strcmp(dvar->name + len - 5, "_load"))
208                         continue;
209                 if (strcmp(dvar->data[0], "yes") != 0 &&
210                     strcmp(dvar->data[0], "YES") != 0) {
211                         continue;
212                 }
213                 argv[0] = "load";
214                 argv[1] = strdup(dvar->name);
215                 argv[1][len - 5] = 0;
216                 tmp = perform(2, argv);
217                 free(argv[1]);
218                 if (tmp != CMD_OK) {
219                         time_t t = time(NULL);
220                         printf("Unable to load %s%s\n", DirBase, argv[1]);
221                         while (time(NULL) == t)
222                                 ;
223                         /* don't kill the boot sequence */
224                         /* res = tmp; */
225                 }
226
227         }
228         return(res);
229 }
230
231 /*
232  * Clear all menus
233  */
234 static int
235 command_menuclear(int ac, char **av)
236 {
237         dvar_unset("menu_*");
238         dvar_unset("item_*");
239         curitem = 0;
240         curadd = 0;
241         return(0);
242 }
243
244 /*
245  * Add menu bullet
246  */
247 static int
248 command_menuitem(int ac, char **av)
249 {
250         char namebuf[32];
251         char *cp;
252
253         if (ac != 3) {
254                 sprintf(command_errbuf, "Bad menuitem syntax");
255                 return (CMD_ERROR);
256         }
257         curitem = (unsigned char)av[1][0];
258         if (curitem == 0) {
259                 sprintf(command_errbuf, "Bad menuitem syntax");
260                 return (CMD_ERROR);
261         }
262         snprintf(namebuf, sizeof(namebuf), "menu_%c", curitem);
263         dvar_set(namebuf, &av[2], 1);
264         curadd = 0;
265
266         return(CMD_OK);
267 }
268
269 /*
270  * Add execution item
271  */
272 static int
273 command_menuadd(int ac, char **av)
274 {
275         char namebuf[32];
276         int i;
277
278         if (ac == 1)
279                 return(CMD_OK);
280         if (curitem == 0) {
281                 sprintf(command_errbuf, "Missing menuitem for menuadd");
282                 return(CMD_ERROR);
283         }
284         snprintf(namebuf, sizeof(namebuf), "item_%c_%d", curitem, curadd);
285         dvar_set(namebuf, &av[1], ac - 1);
286         ++curadd;
287         return (CMD_OK);
288 }
289
290 /*
291  * Execute menu system
292  */
293 static int
294 command_menu(int ac, char **av)
295 {
296         int timeout = -1;
297         time_t time_target;
298         time_t time_last;
299         time_t t;
300         char *cp;
301         int c;
302         int res;
303         int counting = 1;
304
305         menu_display();
306         if ((cp = getenv("autoboot_delay")) != NULL)
307                 timeout = strtol(cp, NULL, 0);
308         if (timeout <= 0)
309                 timeout = 10;
310         if (timeout > 24 * 60 * 60)
311                 timeout = 24 * 60 * 60;
312
313         time_target = time(NULL) + timeout;
314         time_last = 0;
315         c = '1';
316         for (;;) {
317                 if (ischar()) {
318                         c = getchar();
319                         if (c == '\r' || c == '\n') {
320                                 c = '1';
321                                 break;
322                         }
323                         if (c == ' ') {
324                                 if (counting) {
325                                         printf("\rCountdown halted by "
326                                                "space   ");
327                                 }
328                                 counting = 0;
329                                 continue;
330                         }
331                         if (c == 0x1b) {
332                                 setenv("autoboot_delay", "NO", 1);
333                                 return(CMD_OK);
334                         }
335                         res = menu_execute(c);
336                         if (res >= 0) {
337                                 setenv("autoboot_delay", "NO", 1);
338                                 return(CMD_OK);
339                         }
340                         /* else ignore char */
341                 }
342                 if (counting) {
343                         t = time(NULL);
344                         if (time_last == t)
345                                 continue;
346                         time_last = t;
347                         printf("\rBooting in %d second%s... ",
348                                 (int)(time_target - t),
349                                 ((time_target - t) == 1 ? "" : "s"));
350                         if ((int)(time_target - t) <= 0) {
351                                 c = '1';
352                                 break;
353                         }
354                 }
355         }
356         res = menu_execute(c);
357         if (res != CMD_OK)
358                 setenv("autoboot_delay", "NO", 1);
359         return (res);
360 }
361
362 static void
363 menu_display(void)
364 {
365         dvar_t dvar;
366
367         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
368                 if (strncmp(dvar->name, "menu_", 5) == 0) {
369                         printf("%c. %s\n", dvar->name[5], dvar->data[0]);
370                 }
371         }
372         printf("\n");
373 }
374
375 static int
376 menu_execute(int c)
377 {
378         dvar_t dvar;
379         char namebuf[32];
380         int res;
381
382         printf("\n");
383
384         snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
385
386         /*
387          * Does this menu option exist?
388          */
389         if (dvar_get(namebuf) == NULL)
390                 return(-1);
391
392         snprintf(namebuf, sizeof(namebuf), "item_%c", c);
393         res = CMD_OK;
394         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
395                 if (strncmp(dvar->name, namebuf, 6) == 0) {
396                         res = perform(dvar->count, dvar->data);
397                         if (res != CMD_OK) {
398                                 printf("%s: %s\n",
399                                         dvar->data[0], command_errmsg);
400                                 setenv("autoboot_delay", "NO", 1);
401                                 break;
402                         }
403                 }
404         }
405         return(res);
406 }