1 /*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/boot/common/commands.c,v 1.19 2003/08/25 23:30:41 obrien Exp $ 27 */ 28 29 #include <stand.h> 30 #include <string.h> 31 32 #include "bootstrap.h" 33 34 char *command_errmsg; 35 char command_errbuf[COMMAND_ERRBUFSZ]; 36 int CurrentCondition = 1; 37 38 static int page_file(char *filename); 39 40 /* 41 * Help is read from a formatted text file. 42 * 43 * Entries in the file are formatted as 44 45 # Ttopic [Ssubtopic] Ddescription 46 help 47 text 48 here 49 # 50 51 * 52 * Note that for code simplicity's sake, the above format must be followed 53 * exactly. 54 * 55 * Subtopic entries must immediately follow the topic (this is used to 56 * produce the listing of subtopics). 57 * 58 * If no argument(s) are supplied by the user, the help for 'help' is displayed. 59 */ 60 COMMAND_SET(help, "help", "detailed help", command_help); 61 62 static int 63 help_getnext(int fd, char **topic, char **subtopic, char **desc) 64 { 65 char line[81], *cp, *ep; 66 67 for (;;) { 68 if (fgetstr(line, 80, fd) < 0) 69 return(0); 70 71 if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' ')) 72 continue; 73 74 *topic = *subtopic = *desc = NULL; 75 cp = line + 2; 76 while ((cp != NULL) && (*cp != 0)) { 77 ep = strchr(cp, ' '); 78 if ((*cp == 'T') && (*topic == NULL)) { 79 if (ep != NULL) 80 *ep++ = 0; 81 *topic = strdup(cp + 1); 82 } else if ((*cp == 'S') && (*subtopic == NULL)) { 83 if (ep != NULL) 84 *ep++ = 0; 85 *subtopic = strdup(cp + 1); 86 } else if (*cp == 'D') { 87 *desc = strdup(cp + 1); 88 ep = NULL; 89 } 90 cp = ep; 91 } 92 if (*topic == NULL) { 93 if (*subtopic != NULL) 94 free(*subtopic); 95 if (*desc != NULL) 96 free(*desc); 97 } else { 98 return(1); 99 } 100 } 101 } 102 103 static void 104 help_emitsummary(char *topic, char *subtopic, char *desc) 105 { 106 int i; 107 108 pager_output(" "); 109 pager_output(topic); 110 i = strlen(topic); 111 if (subtopic != NULL) { 112 pager_output(" "); 113 pager_output(subtopic); 114 i += strlen(subtopic) + 1; 115 } 116 if (desc != NULL) { 117 do { 118 pager_output(" "); 119 } while (i++ < 30); 120 pager_output(desc); 121 } 122 pager_output("\n"); 123 } 124 125 126 static int 127 command_help(int argc, char *argv[]) 128 { 129 char buf[81]; /* XXX buffer size? */ 130 int hfd, matched, doindex; 131 char *topic, *subtopic, *t, *s, *d; 132 133 /* page the help text from our load path */ 134 /* sprintf(buf, "%s/boot/loader.help", getenv("loaddev")); */ 135 /* page the help text from our base path */ 136 snprintf(buf, sizeof(buf), "%sloader.help", getenv("base")); 137 if ((hfd = open(buf, O_RDONLY)) < 0) { 138 if ((hfd = rel_open("loader.help", NULL, O_RDONLY)) < 0) { 139 printf("Verbose help not available, use '?' to list commands\n"); 140 return(CMD_OK); 141 } 142 } 143 144 /* pick up request from arguments */ 145 topic = subtopic = NULL; 146 switch(argc) { 147 case 3: 148 subtopic = strdup(argv[2]); 149 /* FALLTHROUGH */ 150 case 2: 151 topic = strdup(argv[1]); 152 break; 153 case 1: 154 topic = strdup("help"); 155 break; 156 default: 157 command_errmsg = "usage is 'help <topic> [<subtopic>]"; 158 return(CMD_ERROR); 159 } 160 161 /* magic "index" keyword */ 162 doindex = !strcmp(topic, "index"); 163 matched = doindex; 164 165 /* Scan the helpfile looking for help matching the request */ 166 pager_open(); 167 while (help_getnext(hfd, &t, &s, &d)) { 168 169 if (doindex) { /* dink around formatting */ 170 help_emitsummary(t, s, d); 171 172 } else if (strcmp(topic, t)) { 173 /* topic mismatch */ 174 if (matched) /* nothing more on this topic, stop scanning */ 175 break; 176 177 } else { 178 /* topic matched */ 179 matched = 1; 180 if (((subtopic == NULL) && (s == NULL)) || 181 ((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) { 182 /* exact match, print text */ 183 while ((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) { 184 if (pager_output(buf)) 185 break; 186 if (pager_output("\n")) 187 break; 188 } 189 } else if ((subtopic == NULL) && (s != NULL)) { 190 /* topic match, list subtopics */ 191 help_emitsummary(t, s, d); 192 } 193 } 194 free(t); 195 free(s); 196 free(d); 197 } 198 pager_close(); 199 close(hfd); 200 if (!matched) { 201 snprintf(command_errbuf, sizeof(command_errbuf), 202 "no help available for '%s'", topic); 203 free(topic); 204 if (subtopic) 205 free(subtopic); 206 return(CMD_ERROR); 207 } 208 free(topic); 209 if (subtopic) 210 free(subtopic); 211 return(CMD_OK); 212 } 213 214 215 COMMAND_SET(commandlist, "?", "list commands", command_commandlist); 216 217 static int 218 command_commandlist(int argc, char *argv[]) 219 { 220 struct bootblk_command **cmdp; 221 char str[81]; 222 223 pager_open(); 224 printf("Available commands:\n"); 225 SET_FOREACH(cmdp, Xcommand_set) { 226 if (((*cmdp)->c_name != NULL) && ((*cmdp)->c_desc != NULL)) { 227 snprintf(str, sizeof(str), " %-15s %s\n", 228 (*cmdp)->c_name, (*cmdp)->c_desc); 229 pager_output(str); 230 } 231 } 232 pager_close(); 233 return(CMD_OK); 234 } 235 236 /* 237 * XXX set/show should become set/echo if we have variable 238 * substitution happening. 239 */ 240 241 COMMAND_SET(show, "show", "show kenv variable(s)", command_show); 242 243 static int 244 command_show(int argc, char *argv[]) 245 { 246 struct env_var *ev; 247 char *cp; 248 249 if (argc < 2) { 250 /* 251 * With no arguments, print everything. 252 */ 253 pager_open(); 254 for (ev = environ; ev != NULL; ev = ev->ev_next) { 255 pager_output(ev->ev_name); 256 cp = getenv(ev->ev_name); 257 if (cp != NULL) { 258 pager_output("="); 259 pager_output(cp); 260 } 261 if (pager_output("\n")) 262 break; 263 } 264 pager_close(); 265 } else { 266 if ((cp = getenv(argv[1])) != NULL) { 267 printf("%s\n", cp); 268 } else { 269 snprintf(command_errbuf, sizeof(command_errbuf), 270 "variable '%s' not found", argv[1]); 271 return(CMD_ERROR); 272 } 273 } 274 return(CMD_OK); 275 } 276 277 COMMAND_SET(set, "set", "set a kenv variable", command_set); 278 279 static int 280 command_set(int argc, char *argv[]) 281 { 282 int err; 283 284 if (argc != 2) { 285 command_errmsg = "wrong number of arguments"; 286 return(CMD_ERROR); 287 } else { 288 if ((err = putenv(argv[1])) != 0) { 289 command_errmsg = strerror(err); 290 return(CMD_ERROR); 291 } 292 } 293 return(CMD_OK); 294 } 295 296 COMMAND_SET(unset, "unset", "unset a kenv variable", command_unset); 297 298 static int 299 command_unset(int argc, char *argv[]) 300 { 301 if (argc != 2) { 302 command_errmsg = "wrong number of arguments"; 303 return(CMD_ERROR); 304 } else { 305 /* ignore any errors */ 306 unsetenv(argv[1]); 307 } 308 return(CMD_OK); 309 } 310 311 COMMAND_SET(echo, "echo", "print text ($VAR is kenv variable)", command_echo); 312 313 static int 314 command_echo(int argc, char *argv[]) 315 { 316 char *s; 317 int nl, ch; 318 319 nl = 0; 320 optind = 1; 321 optreset = 1; 322 while ((ch = getopt(argc, argv, "n")) != -1) { 323 switch(ch) { 324 case 'n': 325 nl = 1; 326 break; 327 case '?': 328 default: 329 /* getopt has already reported an error */ 330 return(CMD_OK); 331 } 332 } 333 argv += (optind); 334 argc -= (optind); 335 336 s = unargv(argc, argv); 337 if (s != NULL) { 338 printf("%s", s); 339 free(s); 340 } 341 if (!nl) 342 printf("\n"); 343 return(CMD_OK); 344 } 345 346 /* 347 * A passable emulation of the sh(1) command of the same name. 348 */ 349 350 COMMAND_SET(read, "read", "read to kenv variable", command_read); 351 352 static int 353 command_read(int argc, char *argv[]) 354 { 355 char *prompt; 356 int timeout; 357 time_t when; 358 char *cp; 359 char *name; 360 char buf[256]; /* XXX size? */ 361 int c; 362 363 timeout = -1; 364 prompt = NULL; 365 optind = 1; 366 optreset = 1; 367 while ((c = getopt(argc, argv, "p:t:")) != -1) { 368 switch(c) { 369 370 case 'p': 371 prompt = optarg; 372 break; 373 case 't': 374 timeout = strtol(optarg, &cp, 0); 375 if (cp == optarg) { 376 snprintf(command_errbuf, sizeof(command_errbuf), 377 "bad timeout '%s'", optarg); 378 return(CMD_ERROR); 379 } 380 break; 381 default: 382 return(CMD_OK); 383 } 384 } 385 386 argv += (optind); 387 argc -= (optind); 388 if (argc > 1) { 389 command_errmsg = "wrong number of arguments"; 390 return(CMD_ERROR); 391 } 392 name = (argc > 0) ? argv[0]: NULL; 393 394 if (prompt != NULL) 395 printf("%s", prompt); 396 if (timeout >= 0) { 397 when = time(NULL) + timeout; 398 while (!ischar()) 399 if (time(NULL) >= when) 400 return(CMD_OK); /* is timeout an error? */ 401 } 402 403 ngets(buf, sizeof(buf)); 404 405 if (name != NULL) 406 setenv(name, buf, 1); 407 return(CMD_OK); 408 } 409 410 /* 411 * File pager 412 */ 413 COMMAND_SET(more, "more", "show contents of a file", command_more); 414 415 static int 416 command_more(int argc, char *argv[]) 417 { 418 int i; 419 int res; 420 char line[80]; 421 422 res=0; 423 pager_open(); 424 for (i = 1; (i < argc) && (res == 0); i++) { 425 sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]); 426 if (pager_output(line)) 427 break; 428 res = page_file(argv[i]); 429 if (!res) { 430 sprintf(line, "*** FILE %s END ***\n", argv[i]); 431 res = pager_output(line); 432 } 433 } 434 pager_close(); 435 436 if (res == 0) 437 return CMD_OK; 438 else 439 return CMD_ERROR; 440 } 441 442 static int 443 page_file(char *filename) 444 { 445 int result; 446 int fd; 447 char *fullpath; 448 449 if ((fd = rel_open(filename, &fullpath, O_RDONLY)) != -1) { 450 close(fd); 451 result = pager_file(fullpath); 452 free(fullpath); 453 } else { 454 result = -1; 455 } 456 if (result == -1) { 457 snprintf(command_errbuf, sizeof(command_errbuf), 458 "error showing %s", filename); 459 } 460 461 return result; 462 } 463 464 /* 465 * List all disk-like devices 466 */ 467 COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); 468 469 static int 470 command_lsdev(int argc, char *argv[]) 471 { 472 int verbose, ch, i; 473 char line[80]; 474 475 verbose = 0; 476 optind = 1; 477 optreset = 1; 478 while ((ch = getopt(argc, argv, "v")) != -1) { 479 switch(ch) { 480 case 'v': 481 verbose = 1; 482 break; 483 case '?': 484 default: 485 /* getopt has already reported an error */ 486 return(CMD_OK); 487 } 488 } 489 argv += (optind); 490 argc -= (optind); 491 492 pager_open(); 493 for (i = 0; devsw[i] != NULL; i++) { 494 if (devsw[i]->dv_print != NULL){ 495 sprintf(line, "%s devices:\n", devsw[i]->dv_name); 496 if (pager_output(line)) 497 break; 498 devsw[i]->dv_print(verbose); 499 } else { 500 sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name); 501 if (pager_output(line)) 502 break; 503 } 504 } 505 pager_close(); 506 return(CMD_OK); 507 } 508 509 /* 510 * CONDITIONALS 511 */ 512 COMMAND_SET_COND(ifexists, "ifexists", "conditional file/dir present", 513 command_ifexists); 514 515 struct cond { 516 char inherit; 517 char current; 518 }; 519 520 static struct cond CondStack[32]; 521 static int CondIndex; 522 523 static int 524 command_ifexists(int argc, char *argv[]) 525 { 526 if (CondIndex + 1 == sizeof(CondStack)/sizeof(CondStack[0])) { 527 sprintf(command_errbuf, "if stack too deep"); 528 return(-1); 529 } else if (argc != 2) { 530 sprintf(command_errbuf, "ifexists requires one argument"); 531 return(-1); 532 } else { 533 struct stat sb; 534 struct cond *cond = &CondStack[CondIndex++]; 535 536 cond->inherit = CurrentCondition; 537 538 if (rel_stat(argv[1], &sb)) { 539 cond->current = 0; 540 } else { 541 cond->current = 1; 542 } 543 CurrentCondition = (cond->inherit && cond->current); 544 return(CMD_OK); 545 } 546 } 547 548 COMMAND_SET_COND(ifset, "ifset", "conditional kenv variable present", command_ifset); 549 550 static int 551 command_ifset(int argc, char *argv[]) 552 { 553 if (CondIndex + 1 == sizeof(CondStack)/sizeof(CondStack[0])) { 554 sprintf(command_errbuf, "if stack too deep"); 555 return(-1); 556 } else if (argc != 2) { 557 sprintf(command_errbuf, "ifset requires one argument"); 558 return(-1); 559 } else { 560 struct cond *cond = &CondStack[CondIndex++]; 561 562 cond->inherit = CurrentCondition; 563 564 if (getenv(argv[1])) { 565 cond->current = 1; 566 } else { 567 cond->current = 0; 568 } 569 CurrentCondition = (cond->inherit && cond->current); 570 return(CMD_OK); 571 } 572 } 573 574 COMMAND_SET_COND(elseifexists, "elseifexists", "conditional file/dir present", 575 command_elseifexists); 576 577 static int 578 command_elseifexists(int argc, char *argv[]) 579 { 580 if (CondIndex == 0) { 581 sprintf(command_errbuf, "elseifexists without if"); 582 return(-1); 583 } else if (argc != 2) { 584 sprintf(command_errbuf, "elseifexists requires one argument"); 585 return(-1); 586 } else { 587 struct stat sb; 588 struct cond *cond = &CondStack[CondIndex - 1]; 589 590 if (cond->inherit == 0) { 591 CurrentCondition = 0; /* already ran / can't run */ 592 } else if (cond->current) { 593 cond->inherit = 0; /* can't run any more */ 594 cond->current = 0; 595 CurrentCondition = 0; 596 } else { 597 if (rel_stat(argv[1], &sb)) { 598 cond->current = 0; 599 } else { 600 cond->current = 1; 601 } 602 CurrentCondition = (cond->inherit && cond->current); 603 } 604 return(CMD_OK); 605 } 606 } 607 608 COMMAND_SET_COND(else, "else", "conditional if/else/endif", command_else); 609 610 static int 611 command_else(int argc, char *argv[]) 612 { 613 struct cond *cond; 614 615 if (CondIndex) { 616 cond = &CondStack[CondIndex - 1]; 617 cond->current = !cond->current; 618 CurrentCondition = (cond->inherit && cond->current); 619 return(CMD_OK); 620 } else { 621 sprintf(command_errbuf, "else without if"); 622 return(-1); 623 } 624 } 625 626 COMMAND_SET_COND(endif, "endif", "conditional if/else/endif", command_endif); 627 628 static int 629 command_endif(int argc, char *argv[]) 630 { 631 struct cond *cond; 632 633 if (CondIndex) { 634 --CondIndex; 635 if (CondIndex) { 636 cond = &CondStack[CondIndex - 1]; 637 CurrentCondition = (cond->inherit && cond->current); 638 } else { 639 CurrentCondition = 1; 640 } 641 return(CMD_OK); 642 } else { 643 sprintf(command_errbuf, "endif without if"); 644 return(-1); 645 } 646 } 647 648 COMMAND_SET(rmem, "rmem", "read memory", command_rmem); 649 650 static int 651 command_rmem(int argc, char *argv[]) 652 { 653 int addr; 654 655 if (argc > 1) { 656 addr = strtol(argv[1], NULL, 0); 657 printf("%08x: %08x\n", addr, *(int *)(intptr_t)addr); 658 } 659 return(CMD_OK); 660 } 661 662 COMMAND_SET(wmem, "wmem", "write memory", command_wmem); 663 664 static int 665 command_wmem(int argc, char *argv[]) 666 { 667 int addr; 668 int data; 669 670 if (argc > 2) { 671 addr = strtol(argv[1], NULL, 0); 672 data = strtol(argv[2], NULL, 0); 673 *(int *)(intptr_t)addr = data; 674 printf("%08x: %08x\n", addr, *(int *)(intptr_t)addr); 675 } 676 return(CMD_OK); 677 } 678