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_verbose", 78 "boot_vidcons", 79 "bootfile", 80 "console", 81 "currdev", 82 "default_kernel", 83 "dumpdev", 84 "ehci_load", 85 "interpret", 86 "init_chroot", 87 "init_path", 88 "kernel_options", 89 "kernelname", 90 "loaddev", 91 "local_modules", 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 "[31;1m ,--, ,--, [0m", 460 "[31;1m | `-, [33;1m_[31m:[33;1m_[31;1m ,-' | [0m", 461 "[31;1m `, `-, [33;1m([31m/ \\[33;1m)[31;1m ,-' ,' [0m", 462 "[31;1m `-, `-,[31m/ \\[31;1m,-' ,-' [0m", 463 "[31;1m `------[31m{ }[31;1m------' [0m", 464 "[31;1m ,----------[31m{ }[31;1m----------, [0m", 465 "[31;1m | _,-[31m{ }[31;1m-,_ | [0m", 466 "[31;1m `-,__,-' [31m\\ /[31;1m `-,__,-' [0m", 467 "[31m | | [0m", 468 "[31m | | [0m", 469 "[31m | | [0m", 470 "[31m | | [0m", 471 "[31m | | [0m", 472 "[31m | | [0m", 473 "[31m `,' [0m", 474 " " }; 475 476 static char *logo_indigo[LOGO_LINES] = { 477 "[36m ,--, ,--[36;1m, [0m", 478 "[36m | `-, [34;1m_[34m:[34;1m_[36m ,-' [36;1m| [0m", 479 "[36m `, `-, [34;1m([34m/ \\[34;1m)[36m ,-' [36;1m,' [0m", 480 "[36m `-, `-,[34m/ \\[36m,-' [36;1m,-' [0m", 481 "[36m `------[34m{ }[36m------[36;1m' [0m", 482 "[36m ,----------[34m{ }[36m----------[36;1m, [0m", 483 "[36m | [36;1m_,-[34m{ }[36m-,_ [36;1m| [0m", 484 "[36m `-,__[36;1m,-' [34m\\ /[36m `-,__[36;1m,-' [0m", 485 "[34m | | [0m", 486 "[34m | | [0m", 487 "[34m | | [0m", 488 "[34m | | [0m", 489 "[34m | | [0m", 490 "[34m | | [0m", 491 "[34m `,' [0m", 492 " " }; 493 494 static char *logo_mono[LOGO_LINES] = { 495 " ,--, ,--, ", 496 " | `-, _:_ ,-' | ", 497 " `, `-, (/ \\) ,-' ,' ", 498 " `-, `-,/ \\,-' ,-' ", 499 " `------{ }------' ", 500 " ,----------{ }----------, ", 501 " | _,-{ }-,_ | ", 502 " `-,__,-' \\ / `-,__,-' ", 503 " | | ", 504 " | | ", 505 " | | ", 506 " | | ", 507 " | | ", 508 " | | ", 509 " `,' ", 510 " " }; 511 512 static void 513 logo_display(char **logo, int line, int orientation, int barrier) 514 { 515 const char *fmt; 516 517 if (orientation == FRED_LEFT) 518 fmt = barrier ? "%s | " : " %s "; 519 else 520 fmt = barrier ? " | %s" : " %s "; 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 530 static void 531 menu_display(void) 532 { 533 dvar_t dvar; 534 int i; 535 int logo_left = 0; /* default to fred on right */ 536 int separated = 0; /* default blue fred without line */ 537 char **logo = logo_indigo; 538 char *console_val = getenv("console"); 539 540 if (dvar_istrue(dvar_get("fred_is_red"))) 541 logo = logo_color; 542 543 if (dvar_istrue(dvar_get("loader_plain"))) 544 logo = logo_mono; 545 546 if (strcmp(console_val, "comconsole") == 0) 547 logo = logo_mono; 548 549 if (dvar_istrue(dvar_get("fred_disable"))) 550 logo = NULL; 551 552 if (dvar_istrue(dvar_get("fred_on_left"))) 553 logo_left = 1; 554 555 if (dvar_istrue(dvar_get("fred_separated"))) 556 separated = 1; 557 558 dvar = dvar_first(); 559 i = 0; 560 561 if (logo != NULL) { 562 if (logo_left) 563 printf(separated ? "%35s|%43s\n" : "%35s %43s\n", 564 " ", " "); 565 else 566 printf(separated ? "%43s|%35s\n" : "%43s %35s\n", 567 " ", " "); 568 } 569 570 while (dvar || i < LOGO_LINES) { 571 if (logo_left) 572 logo_display(logo, i, FRED_LEFT, separated); 573 574 while (dvar) { 575 if (strncmp(dvar->name, "menu_", 5) == 0) { 576 printf(" %c. %-38.38s", 577 dvar->name[5], dvar->data[0]); 578 dvar = dvar_next(dvar); 579 break; 580 } 581 dvar = dvar_next(dvar); 582 } 583 /* 584 * Pad when the number of menu entries is less than 585 * LOGO_LINES. 586 */ 587 if (dvar == NULL) 588 printf(" %38.38s", " "); 589 590 if (!logo_left) 591 logo_display(logo, i, FRED_RIGHT, separated); 592 printf("\n"); 593 i++; 594 } 595 } 596 597 static int 598 menu_execute(int c) 599 { 600 dvar_t dvar; 601 dvar_t dvar_exec = NULL; 602 dvar_t *dvar_execp = &dvar_exec; 603 char namebuf[32]; 604 int res; 605 606 snprintf(namebuf, sizeof(namebuf), "item_%c_0", c); 607 608 /* 609 * Does this menu option exist? 610 */ 611 if (dvar_get(namebuf) == NULL) 612 return(-1); 613 614 snprintf(namebuf, sizeof(namebuf), "item_%c", c); 615 res = CMD_OK; 616 printf("\n"); 617 618 /* 619 * Copy the items to execute (the act of execution may modify our 620 * local variables so we need to copy). 621 */ 622 for (dvar = dvar_first(); dvar; dvar = dvar_next(dvar)) { 623 if (strncmp(dvar->name, namebuf, 6) == 0) { 624 *dvar_execp = dvar_copy(dvar); 625 dvar_execp = &(*dvar_execp)->next; 626 } 627 } 628 629 /* 630 * Execute items 631 */ 632 for (dvar = dvar_exec; dvar; dvar = dvar->next) { 633 res = perform(dvar->count, dvar->data); 634 if (res != CMD_OK) { 635 printf("%s: %s\n", 636 dvar->data[0], command_errmsg); 637 setenv("autoboot_delay", "NO", 1); 638 break; 639 } 640 } 641 642 /* 643 * Free items 644 */ 645 while (dvar_exec) 646 dvar_free(&dvar_exec); 647 648 return(res); 649 } 650