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