dloader: Add a new option for color loader, "fred_is_blue"
[dragonfly.git] / sys / boot / dloader / cmds.c
CommitLineData
116b7e3c
MD
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
40static void menu_display(void);
41static 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 */
47void
48dloader_init_cmds(void)
49{
50}
51
52/*
d9f21820 53 * "local" intercepts assignments: lines of the form 'a=b'
116b7e3c
MD
54 */
55COMMAND_SET(local, "local", "List local variables", command_local);
b5ef8da1 56COMMAND_SET(lunset, "lunset", "Unset local variable", command_lunset);
d9f21820 57COMMAND_SET(lunsetif, "lunsetif", "Unset local variable if kenv variable is true", command_lunsetif);
116b7e3c
MD
58COMMAND_SET(loadall, "loadall", "Load kernel + modules", command_loadall);
59COMMAND_SET(menuclear, "menuclear", "Clear all menus", command_menuclear);
60COMMAND_SET(menuitem, "menuitem", "Add menu bullet", command_menuitem);
61COMMAND_SET(menuadd, "menuadd", "Add script line for bullet", command_menuadd);
62COMMAND_SET(menu, "menu", "Run menu system", command_menu);
63
64static int curitem;
65static int curadd;
66
8ddd56e2 67static char *kenv_vars[] = {
20134a6f 68 "LINES",
d9f21820 69 "acpi_load",
20134a6f 70 "autoboot_delay",
e6e3bf71
SW
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",
8ddd56e2
SW
80 "bootfile",
81 "console",
82 "currdev",
20134a6f 83 "default_kernel",
d9f21820
TN
84 "dumpdev",
85 "ehci_load",
86 "interpret",
87 "init_chroot",
b67b03f8 88 "init_path",
b00ac785 89 "kernel_options",
20134a6f 90 "kernelname",
8ddd56e2
SW
91 "loaddev",
92 "module_path",
d9f21820
TN
93 "num_ide_disks",
94 "prompt",
95 "rootdev",
20134a6f 96 "root_disk_unit",
445d621d 97 "xhci_load",
8ddd56e2
SW
98 NULL
99};
100
116b7e3c 101/*
b5ef8da1
TN
102 * List or set local variable. Sniff assignment of kenv_vars[] and
103 * loader tunables (recognized by '.' in name).
116b7e3c 104 *
b5ef8da1
TN
105 * format for av[0]:
106 * - List: local
107 * - Set: var=val
116b7e3c
MD
108 */
109static int
110command_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) {
b5ef8da1 122 pager_open();
116b7e3c
MD
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
b5ef8da1
TN
131 pager_output(dvar->name);
132 pager_output("=");
116b7e3c
MD
133 for (i = 0; i < dvar->count; ++i) {
134 if (i)
b5ef8da1
TN
135 pager_output(",");
136 pager_output("\"");
137 pager_output(dvar->data[i]);
138 pager_output("\"");
116b7e3c 139 }
b5ef8da1 140 pager_output("\n");
116b7e3c 141 }
b5ef8da1 142 pager_close();
116b7e3c
MD
143 return(CMD_OK);
144 }
145
146 /*
b5ef8da1 147 * local command intercept for 'var=val'
116b7e3c
MD
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
fc350ba1 162 /*
8ddd56e2
SW
163 * Take care of loader tunables and several other variables,
164 * all of which have to mirror to kenv because libstand or
fc350ba1
MD
165 * other consumers may have hooks into them.
166 */
8ddd56e2
SW
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 }
116b7e3c
MD
176 }
177 return(CMD_OK);
178}
179
180/*
181 * Unset local variables
182 */
183static int
184command_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
193static int
194command_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/*
b5ef8da1 213 * Load the kernel + all modules specified with MODULE_load="YES"
116b7e3c
MD
214 */
215static int
216command_loadall(int ac, char **av)
217{
218 char *argv[4];
285267ca 219 char *mod_name;
63fcbbd4 220 char *mod_fname;
285267ca
AH
221 char *mod_type;
222 char *tmp_str;
223 dvar_t dvar, dvar2;
116b7e3c
MD
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");
b00ac785 237 argv[2] = getenv("kernel_options");
116b7e3c
MD
238 if (argv[1] == NULL)
239 argv[1] = strdup("kernel");
b00ac785 240 res = perform((argv[2] == NULL)?2:3, argv);
116b7e3c 241 free(argv[1]);
b00ac785
AH
242 if (argv[2])
243 free(argv[2]);
116b7e3c
MD
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 }
285267ca
AH
261
262 mod_name = strdup(dvar->name);
263 mod_name[len - 5] = 0;
264 mod_type = NULL;
63fcbbd4 265 mod_fname = NULL;
285267ca
AH
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
63fcbbd4
AH
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
116b7e3c 300 argv[0] = "load";
285267ca
AH
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);
116b7e3c
MD
311 if (tmp != CMD_OK) {
312 time_t t = time(NULL);
285267ca 313 printf("Unable to load %s%s\n", DirBase, mod_name);
116b7e3c
MD
314 while (time(NULL) == t)
315 ;
316 /* don't kill the boot sequence */
317 /* res = tmp; */
318 }
285267ca 319 free(mod_name);
116b7e3c
MD
320 }
321 return(res);
322}
323
324/*
325 * Clear all menus
326 */
327static int
328command_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 */
340static int
341command_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 }
fc350ba1
MD
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);
116b7e3c
MD
355 dvar_set(namebuf, &av[2], 1);
356 curadd = 0;
357
358 return(CMD_OK);
359}
360
361/*
362 * Add execution item
363 */
364static int
365command_menuadd(int ac, char **av)
366{
367 char namebuf[32];
116b7e3c
MD
368
369 if (ac == 1)
370 return(CMD_OK);
fc350ba1
MD
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);
116b7e3c
MD
376 dvar_set(namebuf, &av[1], ac - 1);
377 ++curadd;
378 return (CMD_OK);
379}
380
381/*
382 * Execute menu system
383 */
384static int
385command_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;
fc350ba1 394 int counting = 1;
116b7e3c
MD
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 }
fc350ba1
MD
414 if (c == ' ') {
415 if (counting) {
416 printf("\rCountdown halted by "
417 "space ");
418 }
419 counting = 0;
420 continue;
421 }
116b7e3c
MD
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 }
fc350ba1
MD
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 }
116b7e3c
MD
445 }
446 }
447 res = menu_execute(c);
448 if (res != CMD_OK)
449 setenv("autoboot_delay", "NO", 1);
450 return (res);
451}
452
b5ef8da1 453#define LOGO_LINES 16
d0f33df3
JT
454#define FRED_LEFT 0
455#define FRED_RIGHT 1
456static char *logo_blank_line = " ";
d0f33df3
JT
457
458static char *logo_color[LOGO_LINES] = {
a031988a
JM
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",
d0f33df3
JT
474 " " };
475
a147fd46
JM
476static char *logo_indigo[LOGO_LINES] = {
477 "\e[36m ,--, ,--\e[36;1m, \e[0m",
478 "\e[36m | `-, \e[34;1m_\e[34m:\e[34;1m_\e[36m ,-' \e[36;1m| \e[0m",
479 "\e[36m `, `-, \e[34;1m(\e[34m/ \\\e[34;1m)\e[36m ,-' \e[36;1m,' \e[0m",
480 "\e[36m `-, `-,\e[34m/ \\\e[36m,-' \e[36;1m,-' \e[0m",
481 "\e[36m `------\e[34m{ }\e[36m------\e[36;1m' \e[0m",
482 "\e[36m ,----------\e[34m{ }\e[36m----------\e[36;1m, \e[0m",
483 "\e[36m | \e[36;1m_,-\e[34m{ }\e[36m-,_ \e[36;1m| \e[0m",
484 "\e[36m `-,__\e[36;1m,-' \e[34m\\ /\e[36m `-,__\e[36;1m,-' \e[0m",
485 "\e[34m | | \e[0m",
486 "\e[34m | | \e[0m",
487 "\e[34m | | \e[0m",
488 "\e[34m | | \e[0m",
489 "\e[34m | | \e[0m",
490 "\e[34m | | \e[0m",
491 "\e[34m `,' \e[0m",
492 " " };
493
d0f33df3 494static char *logo_mono[LOGO_LINES] = {
a031988a
JM
495 " ,--, ,--, ",
496 " | `-, _:_ ,-' | ",
d0f33df3
JT
497 " `, `-, (/ \\) ,-' ,' ",
498 " `-, `-,/ \\,-' ,-' ",
a031988a
JM
499 " `------{ }------' ",
500 " ,----------{ }----------, ",
501 " | _,-{ }-,_ | ",
d0f33df3
JT
502 " `-,__,-' \\ / `-,__,-' ",
503 " | | ",
504 " | | ",
505 " | | ",
506 " | | ",
507 " | | ",
508 " | | ",
a031988a 509 " `,' ",
d0f33df3
JT
510 " " };
511
d0f33df3 512static void
a031988a 513logo_display(char **logo, int line, int orientation, int barrier)
d0f33df3
JT
514{
515 const char *fmt;
516
517 if (orientation == FRED_LEFT)
a031988a 518 fmt = barrier ? "%s | " : " %s ";
d0f33df3 519 else
a031988a 520 fmt = barrier ? " | %s" : " %s ";
d0f33df3
JT
521
522 if (logo != NULL) {
523 if (line < LOGO_LINES)
524 printf(fmt, logo[line]);
525 else
526 printf(fmt, logo_blank_line);
527 }
528}
529
116b7e3c
MD
530static void
531menu_display(void)
532{
533 dvar_t dvar;
d0f33df3
JT
534 int i;
535 int logo_left = 0; /* default to fred on right */
a031988a 536 int separated = 0; /* default no line b/w fred & menu */
d0f33df3 537 char **logo = logo_mono;
116b7e3c 538
d0f33df3
JT
539 if (dvar_istrue(dvar_get("loader_color")))
540 logo = logo_color;
541
542 if (dvar_istrue(dvar_get("fred_disable")))
543 logo = NULL;
544
545 if (dvar_istrue(dvar_get("fred_on_left")))
546 logo_left = 1;
547
a147fd46
JM
548 if (dvar_istrue(dvar_get("fred_is_blue")))
549 logo = logo_indigo;
550
a031988a
JM
551 if (dvar_istrue(dvar_get("fred_separated")))
552 separated = 1;
553
d0f33df3
JT
554 dvar = dvar_first();
555 i = 0;
8367409a 556
a031988a
JM
557 if (logo != NULL) {
558 if (logo_left)
559 printf(separated ? "%35s|%43s\n" : "%35s %43s\n",
560 " ", " ");
561 else
562 printf(separated ? "%43s|%35s\n" : "%43s %35s\n",
563 " ", " ");
564 }
d0f33df3
JT
565
566 while (dvar || i < LOGO_LINES) {
567 if (logo_left)
a031988a 568 logo_display(logo, i, FRED_LEFT, separated);
d0f33df3
JT
569
570 while (dvar) {
571 if (strncmp(dvar->name, "menu_", 5) == 0) {
b5ef8da1
TN
572 printf(" %c. %-38.38s",
573 dvar->name[5], dvar->data[0]);
d0f33df3
JT
574 dvar = dvar_next(dvar);
575 break;
576 }
577 dvar = dvar_next(dvar);
578 }
579 /*
580 * Pad when the number of menu entries is less than
a031988a 581 * LOGO_LINES.
d0f33df3 582 */
b5ef8da1 583 if (dvar == NULL)
d0f33df3 584 printf(" %38.38s", " ");
d0f33df3
JT
585
586 if (!logo_left)
a031988a 587 logo_display(logo, i, FRED_RIGHT, separated);
d0f33df3 588 printf("\n");
b5ef8da1 589 i++;
d0f33df3 590 }
116b7e3c
MD
591}
592
593static int
594menu_execute(int c)
595{
596 dvar_t dvar;
5b31b6dc
MD
597 dvar_t dvar_exec = NULL;
598 dvar_t *dvar_execp = &dvar_exec;
116b7e3c
MD
599 char namebuf[32];
600 int res;
601
116b7e3c
MD
602 snprintf(namebuf, sizeof(namebuf), "item_%c_0", c);
603
604 /*
605 * Does this menu option exist?
606 */
607 if (dvar_get(namebuf) == NULL)
608 return(-1);
609
610 snprintf(namebuf, sizeof(namebuf), "item_%c", c);
611 res = CMD_OK;
5b31b6dc
MD
612 printf("\n");
613
614 /*
615 * Copy the items to execute (the act of execution may modify our
616 * local variables so we need to copy).
617 */
116b7e3c
MD
618 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) {
619 if (strncmp(dvar->name, namebuf, 6) == 0) {
5b31b6dc
MD
620 *dvar_execp = dvar_copy(dvar);
621 dvar_execp = &(*dvar_execp)->next;
116b7e3c
MD
622 }
623 }
5b31b6dc
MD
624
625 /*
626 * Execute items
627 */
628 for (dvar = dvar_exec; dvar; dvar = dvar->next) {
629 res = perform(dvar->count, dvar->data);
630 if (res != CMD_OK) {
631 printf("%s: %s\n",
632 dvar->data[0], command_errmsg);
633 setenv("autoboot_delay", "NO", 1);
634 break;
635 }
636 }
637
638 /*
639 * Free items
640 */
641 while (dvar_exec)
642 dvar_free(&dvar_exec);
643
116b7e3c
MD
644 return(res);
645}