24f091ad43683d78851aac1bdcc9ce16a70310c8
[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 variable", 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  * List or set local variable.  Sniff assignment of kenv_vars[] and
90  * loader tunables (recognized by '.' in name).
91  *
92  * format for av[0]:
93  *  - List: local
94  *  - Set:  var=val
95  */
96 static int
97 command_local(int ac, char **av)
98 {
99         char *name;
100         char *data;
101         dvar_t dvar;
102         int i;
103         int j;
104
105         /*
106          * local command executed directly.
107          */
108         if (strcmp(av[0], "local") == 0) {
109                 pager_open();
110                 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
111                         for (j = 1; j < ac; ++j) {
112                                 if (!strncmp(dvar->name, av[j], strlen(av[j])))
113                                         break;
114                         }
115                         if (ac > 1 && j == ac)
116                                 continue;
117
118                         pager_output(dvar->name);
119                         pager_output("=");
120                         for (i = 0; i < dvar->count; ++i) {
121                                 if (i)
122                                         pager_output(",");
123                                 pager_output("\"");
124                                 pager_output(dvar->data[i]);
125                                 pager_output("\"");
126                         }
127                         pager_output("\n");
128                 }
129                 pager_close();
130                 return(CMD_OK);
131         }
132
133         /*
134          * local command intercept for 'var=val'
135          */
136         name = av[0];
137         data = strchr(name, '=');
138         if (data == NULL) {
139                 sprintf(command_errbuf, "Bad variable syntax");
140                 return (CMD_ERROR);
141         }
142         *data++ = 0;
143
144         if (*data)
145                 dvar_set(name, &data, 1);
146         else
147                 dvar_unset(name);
148
149         /*
150          * Take care of loader tunables and several other variables,
151          * all of which have to mirror to kenv because libstand or
152          * other consumers may have hooks into them.
153          */
154         if (strchr(name, '.')) {
155                 setenv(name, data, 1);
156         } else {
157                 for (i = 0; kenv_vars[i] != NULL; i++) {
158                         if (strcmp(name, kenv_vars[i]) == 0) {
159                                 setenv(name, data, 1);
160                                 return(CMD_OK);
161                         }
162                 }
163         }
164         return(CMD_OK);
165 }
166
167 /*
168  * Unset local variables
169  */
170 static int
171 command_lunset(int ac, char **av)
172 {
173         int i;
174
175         for (i = 1; i < ac; ++i)
176                 dvar_unset(av[i]);
177         return(0);
178 }
179
180 static int
181 command_lunsetif(int ac, char **av)
182 {
183         char *envdata;
184
185         if (ac != 3) {
186                 sprintf(command_errbuf,
187                         "syntax error use lunsetif lname envname");
188                 return(CMD_ERROR);
189         }
190         envdata = getenv(av[2]);
191         if (strcmp(envdata, "yes") == 0 ||
192             strcmp(envdata, "YES") == 0 ||
193             strtol(envdata, NULL, 0)) {
194                 dvar_unset(av[1]);
195         }
196         return (CMD_OK);
197 }
198
199 /*
200  * Load the kernel + all modules specified with MODULE_load="YES"
201  */
202 static int
203 command_loadall(int ac, char **av)
204 {
205         char *argv[4];
206         char *mod_name;
207         char *mod_fname;
208         char *mod_type;
209         char *tmp_str;
210         dvar_t dvar, dvar2;
211         int len;
212         int argc;
213         int res;
214         int tmp;
215
216         argv[0] = "unload";
217         (void)perform(1, argv);
218
219         /*
220          * Load kernel
221          */
222         argv[0] = "load";
223         argv[1] = getenv("kernelname");
224         argv[2] = getenv("kernel_options");
225         if (argv[1] == NULL)
226                 argv[1] = strdup("kernel");
227         res = perform((argv[2] == NULL)?2:3, argv);
228         free(argv[1]);
229         if (argv[2])
230                 free(argv[2]);
231
232         if (res != CMD_OK) {
233                 printf("Unable to load %s%s\n", DirBase, argv[1]);
234                 return(res);
235         }
236
237         /*
238          * Load modules
239          */
240         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
241                 len = strlen(dvar->name);
242                 if (len <= 5 || strcmp(dvar->name + len - 5, "_load"))
243                         continue;
244                 if (strcmp(dvar->data[0], "yes") != 0 &&
245                     strcmp(dvar->data[0], "YES") != 0) {
246                         continue;
247                 }
248
249                 mod_name = strdup(dvar->name);
250                 mod_name[len - 5] = 0;
251                 mod_type = NULL;
252                 mod_fname = NULL;
253
254                 /* Check if there's a matching foo_type */
255                 for (dvar2 = dvar_first();
256                      dvar2 && (mod_type == NULL);
257                      dvar2 = dvar_next(dvar2)) {
258                         len = strlen(dvar2->name);
259                         if (len <= 5 || strcmp(dvar2->name + len - 5, "_type"))
260                                 continue;
261                         tmp_str = strdup(dvar2->name);
262                         tmp_str[len - 5] = 0;
263                         if (strcmp(tmp_str, mod_name) == 0)
264                                 mod_type = dvar2->data[0];
265
266                         free(tmp_str);
267                 }
268
269                 /* Check if there's a matching foo_name */
270                 for (dvar2 = dvar_first();
271                      dvar2 && (mod_fname == NULL);
272                      dvar2 = dvar_next(dvar2)) {
273                         len = strlen(dvar2->name);
274                         if (len <= 5 || strcmp(dvar2->name + len - 5, "_name"))
275                                 continue;
276                         tmp_str = strdup(dvar2->name);
277                         tmp_str[len - 5] = 0;
278                         if (strcmp(tmp_str, mod_name) == 0) {
279                                 mod_fname = dvar2->data[0];
280                                 free(mod_name);
281                                 mod_name = strdup(mod_fname);
282                         }
283
284                         free(tmp_str);
285                 }
286
287                 argv[0] = "load";
288                 if (mod_type) {
289                         argc = 4;
290                         argv[1] = "-t";
291                         argv[2] = mod_type;
292                         argv[3] = mod_name;
293                 } else {
294                         argc = 2;
295                         argv[1] = mod_name;
296                 }
297                 tmp = perform(argc, argv);
298                 if (tmp != CMD_OK) {
299                         time_t t = time(NULL);
300                         printf("Unable to load %s%s\n", DirBase, mod_name);
301                         while (time(NULL) == t)
302                                 ;
303                         /* don't kill the boot sequence */
304                         /* res = tmp; */
305                 }
306                 free(mod_name);
307         }
308         return(res);
309 }
310
311 /*
312  * Clear all menus
313  */
314 static int
315 command_menuclear(int ac, char **av)
316 {
317         dvar_unset("menu_*");
318         dvar_unset("item_*");
319         curitem = 0;
320         curadd = 0;
321         return(0);
322 }
323
324 /*
325  * Add menu bullet
326  */
327 static int
328 command_menuitem(int ac, char **av)
329 {
330         char namebuf[32];
331         char *cp;
332
333         if (ac != 3) {
334                 sprintf(command_errbuf, "Bad menuitem syntax");
335                 return (CMD_ERROR);
336         }
337         curitem = (unsigned char)av[1][0];
338         if (curitem == 0) {
339                 sprintf(command_errbuf, "Bad menuitem syntax");
340                 return (CMD_ERROR);
341         }
342         snprintf(namebuf, sizeof(namebuf), "menu_%c", curitem);
343         dvar_set(namebuf, &av[2], 1);
344         curadd = 0;
345
346         return(CMD_OK);
347 }
348
349 /*
350  * Add execution item
351  */
352 static int
353 command_menuadd(int ac, char **av)
354 {
355         char namebuf[32];
356         int i;
357
358         if (ac == 1)
359                 return(CMD_OK);
360         if (curitem == 0) {
361                 sprintf(command_errbuf, "Missing menuitem for menuadd");
362                 return(CMD_ERROR);
363         }
364         snprintf(namebuf, sizeof(namebuf), "item_%c_%d", curitem, curadd);
365         dvar_set(namebuf, &av[1], ac - 1);
366         ++curadd;
367         return (CMD_OK);
368 }
369
370 /*
371  * Execute menu system
372  */
373 static int
374 command_menu(int ac, char **av)
375 {
376         int timeout = -1;
377         time_t time_target;
378         time_t time_last;
379         time_t t;
380         char *cp;
381         int c;
382         int res;
383         int counting = 1;
384         char *argv[4];
385
386         menu_display();
387         if ((cp = getenv("autoboot_delay")) != NULL)
388                 timeout = strtol(cp, NULL, 0);
389         if (timeout <= 0)
390                 timeout = 10;
391         if (timeout > 24 * 60 * 60)
392                 timeout = 24 * 60 * 60;
393
394         time_target = time(NULL) + timeout;
395         time_last = 0;
396         c = '1';
397         for (;;) {
398                 if (ischar()) {
399                         c = getchar();
400                         if (c == '\r' || c == '\n') {
401                                 c = '1';
402                                 break;
403                         }
404                         if (c == ' ') {
405                                 if (counting) {
406                                         printf("\rCountdown halted by "
407                                                "space   ");
408                                 }
409                                 counting = 0;
410                                 continue;
411                         }
412                         if (c == 0x1b) {
413                                 setenv("autoboot_delay", "NO", 1);
414                                 return(CMD_OK);
415                         }
416                         res = menu_execute(c);
417                         if (res >= 0) {
418                                 setenv("autoboot_delay", "NO", 1);
419                                 return(CMD_OK);
420                         }
421                         /* else ignore char */
422                 }
423                 if (counting) {
424                         t = time(NULL);
425                         if (time_last == t)
426                                 continue;
427                         time_last = t;
428                         printf("\rBooting in %d second%s... ",
429                                 (int)(time_target - t),
430                                 ((time_target - t) == 1 ? "" : "s"));
431                         if ((int)(time_target - t) <= 0) {
432                                 c = '1';
433                                 break;
434                         }
435                 }
436         }
437         res = menu_execute(c);
438         if (res != CMD_OK)
439                 setenv("autoboot_delay", "NO", 1);
440         return (res);
441 }
442
443 #define LOGO_LINES 16
444 #define FRED_LEFT 0
445 #define FRED_RIGHT 1
446 static char *logo_blank_line = "                                 ";
447 static char *menu_footer = "===============================================================================\n";
448
449 static char *logo_color[LOGO_LINES] = {
450         "\e[37m ,--,           \e[31m|           \e[37m,--, \e[0m",
451         "\e[37m |   `-,       \e[31m,^,       \e[37m,-'   | \e[0m",
452         "\e[37m  `,    `-,   \e[32m(\e[31m/ \\\e[32m)   \e[37m,-'    ,'  \e[0m",
453         "\e[37m    `-,    `-,\e[31m/   \\\e[37m,-'    ,-'    \e[0m",
454         "\e[37m       `------\e[31m(   )\e[37m------'       \e[0m",
455         "\e[37m   ,----------\e[31m(   )\e[37m----------,   \e[0m",
456         "\e[37m  |        _,-\e[31m(   )\e[37m-,_        |  \e[0m",
457         "\e[37m   `-,__,-'   \e[31m\\   /   \e[37m`-,__,-'   \e[0m",
458         "\e[37m               \e[31m| |               \e[0m",
459         "\e[37m               \e[31m| |               \e[0m",
460         "\e[37m               \e[31m| |               \e[0m",
461         "\e[37m               \e[31m| |               \e[0m",
462         "\e[37m               \e[31m| |               \e[0m",
463         "\e[37m               \e[31m| |               \e[0m",
464         "\e[37m               \e[31m`|'               \e[0m",
465         "                                 " };
466
467 static char *logo_mono[LOGO_LINES] =  {
468         " ,--,           |           ,--, ",
469         " |   `-,       ,^,       ,-'   | ",
470         "  `,    `-,   (/ \\)   ,-'    ,'  ",
471         "    `-,    `-,/   \\,-'    ,-'    ",
472         "       `------(   )------'       ",
473         "   ,----------(   )----------,   ",
474         "  |        _,-(   )-,_        |  ",
475         "   `-,__,-'   \\   /   `-,__,-'   ",
476         "               | |               ",
477         "               | |               ",
478         "               | |               ",
479         "               | |               ",
480         "               | |               ",
481         "               | |               ",
482         "               `|'               ",
483         "                                 " };
484
485 static char *logo_blank[LOGO_LINES] =  {
486         "                                 ",
487         "                                 ",
488         "                                 ",
489         "                                 ",
490         "                                 ",
491         "                                 ",
492         "                                 ",
493         "                                 ",
494         "                                 ",
495         "                                 ",
496         "                                 ",
497         "                                 ",
498         "                                 ",
499         "                                 ",
500         "                                 ",
501         "                                 " };
502
503 static void
504 logo_display(char **logo, int line, int orientation)
505 {
506         const char *fmt;
507
508         if (orientation == FRED_LEFT)
509                 fmt = "%s  | ";
510         else
511                 fmt = " |  %s";
512
513         if (logo != NULL) {
514                 if (line < LOGO_LINES)
515                         printf(fmt, logo[line]);
516                 else
517                         printf(fmt, logo_blank_line);
518         }
519 }
520
521 static void
522 menu_display(void)
523 {
524         dvar_t dvar;
525         int i;
526         int logo_left = 0;              /* default to fred on right */
527         char **logo = logo_mono;
528         const char *fmt;
529
530         if (dvar_istrue(dvar_get("loader_color")))
531                 logo = logo_color;
532
533         if (dvar_istrue(dvar_get("fred_disable")))
534                 logo = NULL;
535
536         if (dvar_istrue(dvar_get("fred_on_left")))
537                 logo_left = 1;
538
539         dvar = dvar_first();
540         i = 0;
541
542         if (logo != NULL)
543                 printf(logo_left ? "%35s|%43s\n" : "%43s|%35s\n", " ", " ");
544
545
546         while (dvar || i < LOGO_LINES) {
547                 if (logo_left)
548                         logo_display(logo, i, FRED_LEFT);
549
550                 while (dvar) {
551                         if (strncmp(dvar->name, "menu_", 5) == 0) {
552                                 printf(" %c. %-38.38s",
553                                     dvar->name[5], dvar->data[0]);
554                                 dvar = dvar_next(dvar);
555                                 break;
556                         }
557                         dvar = dvar_next(dvar);
558                 }
559                 /*
560                  * Pad when the number of menu entries is less than
561                  * LOGO_LINES.
562                  */
563                 if (dvar == NULL)
564                         printf("    %38.38s", " ");
565
566                 if (!logo_left)
567                         logo_display(logo, i, FRED_RIGHT);
568                 printf("\n");
569                 i++;
570         }
571 }
572
573 static int
574 menu_execute(int c)
575 {
576         dvar_t dvar;
577         dvar_t dvar_exec = NULL;
578         dvar_t *dvar_execp = &dvar_exec;
579         char namebuf[32];
580         int res;
581
582         snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
583
584         /*
585          * Does this menu option exist?
586          */
587         if (dvar_get(namebuf) == NULL)
588                 return(-1);
589
590         snprintf(namebuf, sizeof(namebuf), "item_%c", c);
591         res = CMD_OK;
592         printf("\n");
593
594         /*
595          * Copy the items to execute (the act of execution may modify our
596          * local variables so we need to copy).
597          */
598         for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
599                 if (strncmp(dvar->name, namebuf, 6) == 0) {
600                         *dvar_execp = dvar_copy(dvar);
601                         dvar_execp = &(*dvar_execp)->next;
602                 }
603         }
604
605         /*
606          * Execute items
607          */
608         for (dvar = dvar_exec; dvar; dvar = dvar->next) {
609                 res = perform(dvar->count, dvar->data);
610                 if (res != CMD_OK) {
611                         printf("%s: %s\n",
612                                 dvar->data[0], command_errmsg);
613                         setenv("autoboot_delay", "NO", 1);
614                         break;
615                 }
616         }
617
618         /*
619          * Free items
620          */
621         while (dvar_exec)
622                 dvar_free(&dvar_exec);
623
624         return(res);
625 }