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