5f2797476db547160e99935948e080481c430c04
[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 static char *kenv_vars[] = {
68         "boot_askname",
69         "boot_cdrom",
70         "boot_ddb",
71         "boot_gdb",
72         "boot_serial",
73         "boot_single",
74         "boot_userconfig",
75         "boot_verbose",
76         "boot_vidcons",
77         "bootfile",
78         "console",
79         "currdev",
80         "init_path",
81         "kernelname",
82         "kernel_options",
83         "loaddev",
84         "module_path",
85         NULL
86 };
87
88 /*
89  * Set local variable.  Sniff "module_path"
90  *
91  * format a=b (one argument) - in av[0]
92  */
93 static int
94 command_local(int ac, char **av)
95 {
96         char *name;
97         char *data;
98         dvar_t dvar;
99         int i;
100         int j;
101
102         /*
103          * local command executed directly.
104          */
105         if (strcmp(av[0], "local") == 0) {
106                 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
107                         for (j = 1; j < ac; ++j) {
108                                 if (!strncmp(dvar->name, av[j], strlen(av[j])))
109                                         break;
110                         }
111                         if (ac > 1 && j == ac)
112                                 continue;
113
114                         printf("%s=", dvar->name);
115                         for (i = 0; i < dvar->count; ++i) {
116                                 if (i)
117                                         printf(",");
118                                 printf("\"%s\"", dvar->data[i]);
119                         }
120                         printf("\n");
121                 }
122                 return(CMD_OK);
123         }
124
125         /*
126          * local command intercept for blah=blah
127          */
128         name = av[0];
129         data = strchr(name, '=');
130         if (data == NULL) {
131                 sprintf(command_errbuf, "Bad variable syntax");
132                 return (CMD_ERROR);
133         }
134         *data++ = 0;
135
136         if (*data)
137                 dvar_set(name, &data, 1);
138         else
139                 dvar_unset(name);
140
141         /*
142          * Take care of loader tunables and several other variables,
143          * all of which have to mirror to kenv because libstand or
144          * other consumers may have hooks into them.
145          */
146         if (strchr(name, '.')) {
147                 setenv(name, data, 1);
148         } else {
149                 for (i = 0; kenv_vars[i] != NULL; i++) {
150                         if (strcmp(name, kenv_vars[i]) == 0) {
151                                 setenv(name, data, 1);
152                                 return(CMD_OK);
153                         }
154                 }
155         }
156         return(CMD_OK);
157 }
158
159 /*
160  * Unset local variables
161  */
162 static int
163 command_lunset(int ac, char **av)
164 {
165         int i;
166
167         for (i = 1; i < ac; ++i)
168                 dvar_unset(av[i]);
169         return(0);
170 }
171
172 static int
173 command_lunsetif(int ac, char **av)
174 {
175         char *envdata;
176
177         if (ac != 3) {
178                 sprintf(command_errbuf,
179                         "syntax error use lunsetif lname envname");
180                 return(CMD_ERROR);
181         }
182         envdata = getenv(av[2]);
183         if (strcmp(envdata, "yes") == 0 ||
184             strcmp(envdata, "YES") == 0 ||
185             strtol(envdata, NULL, 0)) {
186                 dvar_unset(av[1]);
187         }
188         return (CMD_OK);
189 }
190
191 /*
192  * Load the kernel + all modules specified with
193  */
194 static int
195 command_loadall(int ac, char **av)
196 {
197         char *argv[4];
198         char *mod_name;
199         char *mod_fname;
200         char *mod_type;
201         char *tmp_str;
202         dvar_t dvar, dvar2;
203         int len;
204         int argc;
205         int res;
206         int tmp;
207
208         argv[0] = "unload";
209         (void)perform(1, argv);
210
211         /*
212          * Load kernel
213          */
214         argv[0] = "load";
215         argv[1] = getenv("kernelname");
216         argv[2] = getenv("kernel_options");
217         if (argv[1] == NULL)
218                 argv[1] = strdup("kernel");
219         res = perform((argv[2] == NULL)?2:3, argv);
220         free(argv[1]);
221         if (argv[2])
222                 free(argv[2]);
223
224         if (res != CMD_OK) {
225                 printf("Unable to load %s%s\n", DirBase, argv[1]);
226                 return(res);
227         }
228
229         /*
230          * Load modules
231          */
232         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
233                 len = strlen(dvar->name);
234                 if (len <= 5 || strcmp(dvar->name + len - 5, "_load"))
235                         continue;
236                 if (strcmp(dvar->data[0], "yes") != 0 &&
237                     strcmp(dvar->data[0], "YES") != 0) {
238                         continue;
239                 }
240
241                 mod_name = strdup(dvar->name);
242                 mod_name[len - 5] = 0;
243                 mod_type = NULL;
244                 mod_fname = NULL;
245
246                 /* Check if there's a matching foo_type */
247                 for (dvar2 = dvar_first();
248                      dvar2 && (mod_type == NULL);
249                      dvar2 = dvar_next(dvar2)) {
250                         len = strlen(dvar2->name);
251                         if (len <= 5 || strcmp(dvar2->name + len - 5, "_type"))
252                                 continue;
253                         tmp_str = strdup(dvar2->name);
254                         tmp_str[len - 5] = 0;
255                         if (strcmp(tmp_str, mod_name) == 0)
256                                 mod_type = dvar2->data[0];
257
258                         free(tmp_str);
259                 }
260
261                 /* Check if there's a matching foo_name */
262                 for (dvar2 = dvar_first();
263                      dvar2 && (mod_fname == NULL);
264                      dvar2 = dvar_next(dvar2)) {
265                         len = strlen(dvar2->name);
266                         if (len <= 5 || strcmp(dvar2->name + len - 5, "_name"))
267                                 continue;
268                         tmp_str = strdup(dvar2->name);
269                         tmp_str[len - 5] = 0;
270                         if (strcmp(tmp_str, mod_name) == 0) {
271                                 mod_fname = dvar2->data[0];
272                                 free(mod_name);
273                                 mod_name = strdup(mod_fname);
274                         }
275
276                         free(tmp_str);
277                 }
278
279                 argv[0] = "load";
280                 if (mod_type) {
281                         argc = 4;
282                         argv[1] = "-t";
283                         argv[2] = mod_type;
284                         argv[3] = mod_name;
285                 } else {
286                         argc = 2;
287                         argv[1] = mod_name;
288                 }
289                 tmp = perform(argc, argv);
290                 if (tmp != CMD_OK) {
291                         time_t t = time(NULL);
292                         printf("Unable to load %s%s\n", DirBase, mod_name);
293                         while (time(NULL) == t)
294                                 ;
295                         /* don't kill the boot sequence */
296                         /* res = tmp; */
297                 }
298                 free(mod_name);
299         }
300         return(res);
301 }
302
303 /*
304  * Clear all menus
305  */
306 static int
307 command_menuclear(int ac, char **av)
308 {
309         dvar_unset("menu_*");
310         dvar_unset("item_*");
311         curitem = 0;
312         curadd = 0;
313         return(0);
314 }
315
316 /*
317  * Add menu bullet
318  */
319 static int
320 command_menuitem(int ac, char **av)
321 {
322         char namebuf[32];
323         char *cp;
324
325         if (ac != 3) {
326                 sprintf(command_errbuf, "Bad menuitem syntax");
327                 return (CMD_ERROR);
328         }
329         curitem = (unsigned char)av[1][0];
330         if (curitem == 0) {
331                 sprintf(command_errbuf, "Bad menuitem syntax");
332                 return (CMD_ERROR);
333         }
334         snprintf(namebuf, sizeof(namebuf), "menu_%c", curitem);
335         dvar_set(namebuf, &av[2], 1);
336         curadd = 0;
337
338         return(CMD_OK);
339 }
340
341 /*
342  * Add execution item
343  */
344 static int
345 command_menuadd(int ac, char **av)
346 {
347         char namebuf[32];
348         int i;
349
350         if (ac == 1)
351                 return(CMD_OK);
352         if (curitem == 0) {
353                 sprintf(command_errbuf, "Missing menuitem for menuadd");
354                 return(CMD_ERROR);
355         }
356         snprintf(namebuf, sizeof(namebuf), "item_%c_%d", curitem, curadd);
357         dvar_set(namebuf, &av[1], ac - 1);
358         ++curadd;
359         return (CMD_OK);
360 }
361
362 /*
363  * Execute menu system
364  */
365 static int
366 command_menu(int ac, char **av)
367 {
368         int timeout = -1;
369         time_t time_target;
370         time_t time_last;
371         time_t t;
372         char *cp;
373         int c;
374         int res;
375         int counting = 1;
376
377         menu_display();
378         if ((cp = getenv("autoboot_delay")) != NULL)
379                 timeout = strtol(cp, NULL, 0);
380         if (timeout <= 0)
381                 timeout = 10;
382         if (timeout > 24 * 60 * 60)
383                 timeout = 24 * 60 * 60;
384
385         time_target = time(NULL) + timeout;
386         time_last = 0;
387         c = '1';
388         for (;;) {
389                 if (ischar()) {
390                         c = getchar();
391                         if (c == '\r' || c == '\n') {
392                                 c = '1';
393                                 break;
394                         }
395                         if (c == ' ') {
396                                 if (counting) {
397                                         printf("\rCountdown halted by "
398                                                "space   ");
399                                 }
400                                 counting = 0;
401                                 continue;
402                         }
403                         if (c == 0x1b) {
404                                 setenv("autoboot_delay", "NO", 1);
405                                 return(CMD_OK);
406                         }
407                         res = menu_execute(c);
408                         if (res >= 0) {
409                                 setenv("autoboot_delay", "NO", 1);
410                                 return(CMD_OK);
411                         }
412                         /* else ignore char */
413                 }
414                 if (counting) {
415                         t = time(NULL);
416                         if (time_last == t)
417                                 continue;
418                         time_last = t;
419                         printf("\rBooting in %d second%s... ",
420                                 (int)(time_target - t),
421                                 ((time_target - t) == 1 ? "" : "s"));
422                         if ((int)(time_target - t) <= 0) {
423                                 c = '1';
424                                 break;
425                         }
426                 }
427         }
428         res = menu_execute(c);
429         if (res != CMD_OK)
430                 setenv("autoboot_delay", "NO", 1);
431         return (res);
432 }
433
434 static void
435 menu_display(void)
436 {
437         dvar_t dvar;
438
439         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
440                 if (strncmp(dvar->name, "menu_", 5) == 0) {
441                         printf("%c. %s\n", dvar->name[5], dvar->data[0]);
442                 }
443         }
444         printf("\n");
445 }
446
447 static int
448 menu_execute(int c)
449 {
450         dvar_t dvar;
451         char namebuf[32];
452         int res;
453
454         printf("\n");
455
456         snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
457
458         /*
459          * Does this menu option exist?
460          */
461         if (dvar_get(namebuf) == NULL)
462                 return(-1);
463
464         snprintf(namebuf, sizeof(namebuf), "item_%c", c);
465         res = CMD_OK;
466         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
467                 if (strncmp(dvar->name, namebuf, 6) == 0) {
468                         res = perform(dvar->count, dvar->data);
469                         if (res != CMD_OK) {
470                                 printf("%s: %s\n",
471                                         dvar->data[0], command_errmsg);
472                                 setenv("autoboot_delay", "NO", 1);
473                                 break;
474                         }
475                 }
476         }
477         return(res);
478 }