1 /* 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <errno.h> 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <limits.h> 37 #include <unistd.h> 38 #include <dirent.h> 39 #include <macros.h> 40 #include <sys/systeminfo.h> 41 #include <sys/linker_set.h> 42 #include <sys/queue.h> 43 #include <sys/mnttab.h> 44 #include "loader_emu.h" 45 #include "gfx_fb.h" 46 #include "ficl.h" 47 48 #define MDIR_REMOVED 0x0001 49 #define MDIR_NOHINTS 0x0002 50 51 struct moduledir { 52 char *d_path; /* path of modules directory */ 53 uchar_t *d_hints; /* content of linker.hints file */ 54 int d_hintsz; /* size of hints data */ 55 int d_flags; 56 STAILQ_ENTRY(moduledir) d_link; 57 }; 58 static STAILQ_HEAD(, moduledir) moduledir_list = 59 STAILQ_HEAD_INITIALIZER(moduledir_list); 60 61 static const char *default_searchpath = "/platform/i86pc"; 62 63 static char typestr[] = "?fc?d?b? ?l?s?w"; 64 static int ls_getdir(char **pathp); 65 extern char **_environ; 66 67 char *command_errmsg; 68 char command_errbuf[256]; 69 70 extern void pager_open(void); 71 extern void pager_close(void); 72 extern int pager_output(const char *); 73 extern int pager_file(const char *); 74 static int page_file(char *); 75 static int include(const char *); 76 77 static int command_help(int argc, char *argv[]); 78 static int command_commandlist(int argc, char *argv[]); 79 static int command_show(int argc, char *argv[]); 80 static int command_set(int argc, char *argv[]); 81 static int command_setprop(int argc, char *argv[]); 82 static int command_unset(int argc, char *argv[]); 83 static int command_echo(int argc, char *argv[]); 84 static int command_read(int argc, char *argv[]); 85 static int command_more(int argc, char *argv[]); 86 static int command_ls(int argc, char *argv[]); 87 static int command_include(int argc, char *argv[]); 88 static int command_autoboot(int argc, char *argv[]); 89 static int command_boot(int argc, char *argv[]); 90 static int command_unload(int argc, char *argv[]); 91 static int command_load(int argc, char *argv[]); 92 static int command_reboot(int argc, char *argv[]); 93 static int command_sifting(int argc, char *argv[]); 94 static int command_framebuffer(int argc, char *argv[]); 95 96 #define BF_PARSE 100 97 #define BF_DICTSIZE 30000 98 99 /* update when loader version will change */ 100 static const char bootprog_rev[] = "1.1"; 101 102 /* 103 * BootForth Interface to Ficl Forth interpreter. 104 */ 105 106 ficlSystem *bf_sys; 107 ficlVm *bf_vm; 108 109 /* 110 * Redistribution and use in source and binary forms, with or without 111 * modification, are permitted provided that the following conditions 112 * are met: 113 * 1. Redistributions of source code must retain the above copyright 114 * notice, this list of conditions and the following disclaimer. 115 * 2. Redistributions in binary form must reproduce the above copyright 116 * notice, this list of conditions and the following disclaimer in the 117 * documentation and/or other materials provided with the distribution. 118 * 119 * Jordan K. Hubbard 120 * 29 August 1998 121 * 122 * The meat of the simple parser. 123 */ 124 125 static void clean(void); 126 static int insert(int *argcp, char *buf); 127 128 #define PARSE_BUFSIZE 1024 /* maximum size of one element */ 129 #define MAXARGS 20 /* maximum number of elements */ 130 static char *args[MAXARGS]; 131 132 #define DIGIT(x) \ 133 (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A') 134 135 /* 136 * backslash: Return malloc'd copy of str with all standard "backslash 137 * processing" done on it. Original can be free'd if desired. 138 */ 139 char * 140 backslash(char *str) 141 { 142 /* 143 * Remove backslashes from the strings. Turn \040 etc. into a single 144 * character (we allow eight bit values). Currently NUL is not 145 * allowed. 146 * 147 * Turn "\n" and "\t" into '\n' and '\t' characters. Etc. 148 */ 149 char *new_str; 150 int seenbs = 0; 151 int i = 0; 152 153 if ((new_str = strdup(str)) == NULL) 154 return (NULL); 155 156 while (*str) { 157 if (seenbs) { 158 seenbs = 0; 159 switch (*str) { 160 case '\\': 161 new_str[i++] = '\\'; 162 str++; 163 break; 164 165 /* preserve backslashed quotes, dollar signs */ 166 case '\'': 167 case '"': 168 case '$': 169 new_str[i++] = '\\'; 170 new_str[i++] = *str++; 171 break; 172 173 case 'b': 174 new_str[i++] = '\b'; 175 str++; 176 break; 177 178 case 'f': 179 new_str[i++] = '\f'; 180 str++; 181 break; 182 183 case 'r': 184 new_str[i++] = '\r'; 185 str++; 186 break; 187 188 case 'n': 189 new_str[i++] = '\n'; 190 str++; 191 break; 192 193 case 's': 194 new_str[i++] = ' '; 195 str++; 196 break; 197 198 case 't': 199 new_str[i++] = '\t'; 200 str++; 201 break; 202 203 case 'v': 204 new_str[i++] = '\13'; 205 str++; 206 break; 207 208 case 'z': 209 str++; 210 break; 211 212 case '0': case '1': case '2': case '3': case '4': 213 case '5': case '6': case '7': case '8': case '9': { 214 char val; 215 216 /* Three digit octal constant? */ 217 if (*str >= '0' && *str <= '3' && 218 *(str + 1) >= '0' && *(str + 1) <= '7' && 219 *(str + 2) >= '0' && *(str + 2) <= '7') { 220 221 val = (DIGIT(*str) << 6) + 222 (DIGIT(*(str + 1)) << 3) + 223 DIGIT(*(str + 2)); 224 225 /* 226 * Allow null value if user really 227 * wants to shoot at feet, but beware! 228 */ 229 new_str[i++] = val; 230 str += 3; 231 break; 232 } 233 234 /* 235 * One or two digit hex constant? 236 * If two are there they will both be taken. 237 * Use \z to split them up if this is not 238 * wanted. 239 */ 240 if (*str == '0' && 241 (*(str + 1) == 'x' || *(str + 1) == 'X') && 242 isxdigit(*(str + 2))) { 243 val = DIGIT(*(str + 2)); 244 if (isxdigit(*(str + 3))) { 245 val = (val << 4) + 246 DIGIT(*(str + 3)); 247 str += 4; 248 } else 249 str += 3; 250 /* Yep, allow null value here too */ 251 new_str[i++] = val; 252 break; 253 } 254 } 255 break; 256 257 default: 258 new_str[i++] = *str++; 259 break; 260 } 261 } else { 262 if (*str == '\\') { 263 seenbs = 1; 264 str++; 265 } else 266 new_str[i++] = *str++; 267 } 268 } 269 270 if (seenbs) { 271 /* 272 * The final character was a '\'. 273 * Put it in as a single backslash. 274 */ 275 new_str[i++] = '\\'; 276 } 277 new_str[i] = '\0'; 278 return (new_str); 279 } 280 281 /* 282 * parse: accept a string of input and "parse" it for backslash 283 * substitutions and environment variable expansions (${var}), 284 * returning an argc/argv style vector of whitespace separated 285 * arguments. Returns 0 on success, 1 on failure (ok, ok, so I 286 * wimped-out on the error codes! :). 287 * 288 * Note that the argv array returned must be freed by the caller, but 289 * we own the space allocated for arguments and will free that on next 290 * invocation. This allows argv consumers to modify the array if 291 * required. 292 * 293 * NB: environment variables that expand to more than one whitespace 294 * separated token will be returned as a single argv[] element, not 295 * split in turn. Expanded text is also immune to further backslash 296 * elimination or expansion since this is a one-pass, non-recursive 297 * parser. You didn't specify more than this so if you want more, ask 298 * me. - jkh 299 */ 300 301 #define PARSE_FAIL(expr) \ 302 if (expr) { \ 303 printf("fail at line %d\n", __LINE__); \ 304 clean(); \ 305 free(copy); \ 306 free(buf); \ 307 return (1); \ 308 } 309 310 /* Accept the usual delimiters for a variable, returning counterpart */ 311 static char 312 isdelim(int ch) 313 { 314 if (ch == '{') 315 return ('}'); 316 else if (ch == '(') 317 return (')'); 318 return ('\0'); 319 } 320 321 static int 322 isquote(int ch) 323 { 324 return (ch == '\''); 325 } 326 327 static int 328 isdquote(int ch) 329 { 330 return (ch == '"'); 331 } 332 333 int 334 parse(int *argc, char ***argv, char *str) 335 { 336 int ac; 337 char *val, *p, *q, *copy = NULL; 338 size_t i = 0; 339 char token, tmp, quote, dquote, *buf; 340 enum { STR, VAR, WHITE } state; 341 342 ac = *argc = 0; 343 dquote = quote = 0; 344 if (!str || (p = copy = backslash(str)) == NULL) 345 return (1); 346 347 /* Initialize vector and state */ 348 clean(); 349 state = STR; 350 buf = (char *)malloc(PARSE_BUFSIZE); 351 token = 0; 352 353 /* And awaaaaaaaaay we go! */ 354 while (*p) { 355 switch (state) { 356 case STR: 357 if ((*p == '\\') && p[1]) { 358 p++; 359 PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); 360 buf[i++] = *p++; 361 } else if (isquote(*p)) { 362 quote = quote ? 0 : *p; 363 if (dquote) { /* keep quote */ 364 PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); 365 buf[i++] = *p++; 366 } else 367 ++p; 368 } else if (isdquote(*p)) { 369 dquote = dquote ? 0 : *p; 370 if (quote) { /* keep dquote */ 371 PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); 372 buf[i++] = *p++; 373 } else 374 ++p; 375 } else if (isspace(*p) && !quote && !dquote) { 376 state = WHITE; 377 if (i) { 378 buf[i] = '\0'; 379 PARSE_FAIL(insert(&ac, buf)); 380 i = 0; 381 } 382 ++p; 383 } else if (*p == '$' && !quote) { 384 token = isdelim(*(p + 1)); 385 if (token) 386 p += 2; 387 else 388 ++p; 389 state = VAR; 390 } else { 391 PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); 392 buf[i++] = *p++; 393 } 394 break; 395 396 case WHITE: 397 if (isspace(*p)) 398 ++p; 399 else 400 state = STR; 401 break; 402 403 case VAR: 404 if (token) { 405 PARSE_FAIL((q = strchr(p, token)) == NULL); 406 } else { 407 q = p; 408 while (*q && !isspace(*q)) 409 ++q; 410 } 411 tmp = *q; 412 *q = '\0'; 413 if ((val = getenv(p)) != NULL) { 414 size_t len = strlen(val); 415 416 (void) strncpy(buf + i, val, 417 PARSE_BUFSIZE - (i + 1)); 418 i += min(len, PARSE_BUFSIZE - 1); 419 } 420 *q = tmp; /* restore value */ 421 p = q + (token ? 1 : 0); 422 state = STR; 423 break; 424 } 425 } 426 /* missing terminating ' or " */ 427 PARSE_FAIL(quote || dquote); 428 /* If at end of token, add it */ 429 if (i && state == STR) { 430 buf[i] = '\0'; 431 PARSE_FAIL(insert(&ac, buf)); 432 } 433 args[ac] = NULL; 434 *argc = ac; 435 *argv = (char **)malloc((sizeof (char *) * ac + 1)); 436 bcopy(args, *argv, sizeof (char *) * ac + 1); 437 free(buf); 438 free(copy); 439 return (0); 440 } 441 442 #define MAXARGS 20 443 444 /* Clean vector space */ 445 static void 446 clean(void) 447 { 448 int i; 449 450 for (i = 0; i < MAXARGS; i++) { 451 if (args[i] != NULL) { 452 free(args[i]); 453 args[i] = NULL; 454 } 455 } 456 } 457 458 static int 459 insert(int *argcp, char *buf) 460 { 461 if (*argcp >= MAXARGS) 462 return (1); 463 args[(*argcp)++] = strdup(buf); 464 return (0); 465 } 466 467 static char * 468 isadir(void) 469 { 470 char *buf; 471 size_t bufsize = 20; 472 int ret; 473 474 if ((buf = malloc(bufsize)) == NULL) 475 return (NULL); 476 ret = sysinfo(SI_ARCHITECTURE_K, buf, bufsize); 477 if (ret == -1) { 478 free(buf); 479 return (NULL); 480 } 481 return (buf); 482 } 483 484 /* 485 * Shim for taking commands from BF and passing them out to 'standard' 486 * argv/argc command functions. 487 */ 488 static void 489 bf_command(ficlVm *vm) 490 { 491 char *name, *line, *tail, *cp; 492 size_t len; 493 struct bootblk_command **cmdp; 494 bootblk_cmd_t *cmd; 495 int nstrings, i; 496 int argc, result; 497 char **argv; 498 499 /* Get the name of the current word */ 500 name = vm->runningWord->name; 501 502 /* Find our command structure */ 503 cmd = NULL; 504 SET_FOREACH(cmdp, Xcommand_set) { 505 if (((*cmdp)->c_name != NULL) && 506 strcmp(name, (*cmdp)->c_name) == 0) 507 cmd = (*cmdp)->c_fn; 508 } 509 if (cmd == NULL) 510 printf("callout for unknown command '%s'\n", name); 511 512 /* Check whether we have been compiled or are being interpreted */ 513 if (ficlStackPopInteger(ficlVmGetDataStack(vm))) { 514 /* 515 * Get parameters from stack, in the format: 516 * an un ... a2 u2 a1 u1 n -- 517 * Where n is the number of strings, a/u are pairs of 518 * address/size for strings, and they will be concatenated 519 * in LIFO order. 520 */ 521 nstrings = ficlStackPopInteger(ficlVmGetDataStack(vm)); 522 for (i = 0, len = 0; i < nstrings; i++) 523 len += ficlStackFetch(ficlVmGetDataStack(vm), 524 i * 2).i + 1; 525 line = malloc(strlen(name) + len + 1); 526 (void) strcpy(line, name); 527 528 if (nstrings) 529 for (i = 0; i < nstrings; i++) { 530 len = ficlStackPopInteger( 531 ficlVmGetDataStack(vm)); 532 cp = ficlStackPopPointer( 533 ficlVmGetDataStack(vm)); 534 (void) strcat(line, " "); 535 (void) strncat(line, cp, len); 536 } 537 } else { 538 /* Get remainder of invocation */ 539 tail = ficlVmGetInBuf(vm); 540 for (cp = tail, len = 0; 541 cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++) 542 ; 543 544 line = malloc(strlen(name) + len + 2); 545 (void) strcpy(line, name); 546 if (len > 0) { 547 (void) strcat(line, " "); 548 (void) strncat(line, tail, len); 549 ficlVmUpdateTib(vm, tail + len); 550 } 551 } 552 553 command_errmsg = command_errbuf; 554 command_errbuf[0] = 0; 555 if (!parse(&argc, &argv, line)) { 556 result = (cmd)(argc, argv); 557 free(argv); 558 } else { 559 result = BF_PARSE; 560 } 561 free(line); 562 /* 563 * If there was error during nested ficlExec(), we may no longer have 564 * valid environment to return. Throw all exceptions from here. 565 */ 566 if (result != 0) 567 ficlVmThrow(vm, result); 568 /* This is going to be thrown!!! */ 569 ficlStackPushInteger(ficlVmGetDataStack(vm), result); 570 } 571 572 static char * 573 get_currdev(void) 574 { 575 int ret; 576 char *currdev; 577 FILE *fp; 578 struct mnttab mpref = {0}; 579 struct mnttab mp = {0}; 580 581 mpref.mnt_mountp = "/"; 582 fp = fopen(MNTTAB, "r"); 583 584 /* do the best we can to return something... */ 585 if (fp == NULL) 586 return (strdup(":")); 587 588 ret = getmntany(fp, &mp, &mpref); 589 (void) fclose(fp); 590 if (ret == 0) 591 (void) asprintf(&currdev, "zfs:%s:", mp.mnt_special); 592 else 593 return (strdup(":")); 594 595 return (currdev); 596 } 597 598 /* 599 * Replace a word definition (a builtin command) with another 600 * one that: 601 * 602 * - Throw error results instead of returning them on the stack 603 * - Pass a flag indicating whether the word was compiled or is 604 * being interpreted. 605 * 606 * There is one major problem with builtins that cannot be overcome 607 * in anyway, except by outlawing it. We want builtins to behave 608 * differently depending on whether they have been compiled or they 609 * are being interpreted. Notice that this is *not* the interpreter's 610 * current state. For example: 611 * 612 * : example ls ; immediate 613 * : problem example ; \ "ls" gets executed while compiling 614 * example \ "ls" gets executed while interpreting 615 * 616 * Notice that, though the current state is different in the two 617 * invocations of "example", in both cases "ls" has been 618 * *compiled in*, which is what we really want. 619 * 620 * The problem arises when you tick the builtin. For example: 621 * 622 * : example-1 ['] ls postpone literal ; immediate 623 * : example-2 example-1 execute ; immediate 624 * : problem example-2 ; 625 * example-2 626 * 627 * We have no way, when we get EXECUTEd, of knowing what our behavior 628 * should be. Thus, our only alternative is to "outlaw" this. See RFI 629 * 0007, and ANS Forth Standard's appendix D, item 6.7 for a related 630 * problem, concerning compile semantics. 631 * 632 * The problem is compounded by the fact that "' builtin CATCH" is valid 633 * and desirable. The only solution is to create an intermediary word. 634 * For example: 635 * 636 * : my-ls ls ; 637 * : example ['] my-ls catch ; 638 * 639 * So, with the below implementation, here is a summary of the behavior 640 * of builtins: 641 * 642 * ls -l \ "interpret" behavior, ie, 643 * \ takes parameters from TIB 644 * : ex-1 s" -l" 1 ls ; \ "compile" behavior, ie, 645 * \ takes parameters from the stack 646 * : ex-2 ['] ls catch ; immediate \ undefined behavior 647 * : ex-3 ['] ls catch ; \ undefined behavior 648 * ex-2 ex-3 \ "interpret" behavior, 649 * \ catch works 650 * : ex-4 ex-2 ; \ "compile" behavior, 651 * \ catch does not work 652 * : ex-5 ex-3 ; immediate \ same as ex-2 653 * : ex-6 ex-3 ; \ same as ex-3 654 * : ex-7 ['] ex-1 catch ; \ "compile" behavior, 655 * \ catch works 656 * : ex-8 postpone ls ; immediate \ same as ex-2 657 * : ex-9 postpone ls ; \ same as ex-3 658 * 659 * As the definition below is particularly tricky, and it's side effects 660 * must be well understood by those playing with it, I'll be heavy on 661 * the comments. 662 * 663 * (if you edit this definition, pay attention to trailing spaces after 664 * each word -- I warned you! :-) ) 665 */ 666 #define BUILTIN_CONSTRUCTOR \ 667 ": builtin: " \ 668 ">in @ " /* save the tib index pointer */ \ 669 "' " /* get next word's xt */ \ 670 "swap >in ! " /* point again to next word */ \ 671 "create " /* create a new definition of the next word */ \ 672 ", " /* save previous definition's xt */ \ 673 "immediate " /* make the new definition an immediate word */ \ 674 \ 675 "does> " /* Now, the *new* definition will: */ \ 676 "state @ if " /* if in compiling state: */ \ 677 "1 postpone literal " /* pass 1 flag to indicate compile */ \ 678 "@ compile, " /* compile in previous definition */ \ 679 "postpone throw " /* throw stack-returned result */ \ 680 "else " /* if in interpreting state: */ \ 681 "0 swap " /* pass 0 flag to indicate interpret */ \ 682 "@ execute " /* call previous definition */ \ 683 "throw " /* throw stack-returned result */ \ 684 "then ; " 685 686 extern int ficlExecFD(ficlVm *, int); 687 688 /* 689 * Initialise the Forth interpreter, create all our commands as words. 690 */ 691 ficlVm * 692 bf_init(const char *rc, ficlOutputFunction out) 693 { 694 struct bootblk_command **cmdp; 695 char create_buf[41]; /* 31 characters-long builtins */ 696 char *buf; 697 int fd, rv; 698 ficlSystemInformation *fsi; 699 ficlDictionary *dict; 700 ficlDictionary *env; 701 702 fsi = malloc(sizeof (ficlSystemInformation)); 703 ficlSystemInformationInitialize(fsi); 704 fsi->textOut = out; 705 fsi->dictionarySize = BF_DICTSIZE; 706 707 bf_sys = ficlSystemCreate(fsi); 708 free(fsi); 709 ficlSystemCompileExtras(bf_sys); 710 bf_vm = ficlSystemCreateVm(bf_sys); 711 712 buf = isadir(); 713 if (buf == NULL || strcmp(buf, "amd64") != 0) { 714 (void) setenv("ISADIR", "", 1); 715 } else { 716 (void) setenv("ISADIR", buf, 1); 717 } 718 if (buf != NULL) 719 free(buf); 720 buf = get_currdev(); 721 (void) setenv("currdev", buf, 1); 722 free(buf); 723 724 /* Put all private definitions in a "builtins" vocabulary */ 725 rv = ficlVmEvaluate(bf_vm, 726 "vocabulary builtins also builtins definitions"); 727 if (rv != FICL_VM_STATUS_OUT_OF_TEXT) { 728 printf("error interpreting forth: %d\n", rv); 729 exit(1); 730 } 731 732 /* Builtin constructor word */ 733 rv = ficlVmEvaluate(bf_vm, BUILTIN_CONSTRUCTOR); 734 if (rv != FICL_VM_STATUS_OUT_OF_TEXT) { 735 printf("error interpreting forth: %d\n", rv); 736 exit(1); 737 } 738 739 /* make all commands appear as Forth words */ 740 dict = ficlSystemGetDictionary(bf_sys); 741 cmdp = NULL; 742 743 SET_FOREACH(cmdp, Xcommand_set) { 744 (void) ficlDictionaryAppendPrimitive(dict, 745 (char *)(*cmdp)->c_name, bf_command, FICL_WORD_DEFAULT); 746 rv = ficlVmEvaluate(bf_vm, "forth definitions builtins"); 747 if (rv != FICL_VM_STATUS_OUT_OF_TEXT) { 748 printf("error interpreting forth: %d\n", rv); 749 exit(1); 750 } 751 (void) snprintf(create_buf, sizeof (create_buf), "builtin: %s", 752 (*cmdp)->c_name); 753 rv = ficlVmEvaluate(bf_vm, create_buf); 754 if (rv != FICL_VM_STATUS_OUT_OF_TEXT) { 755 printf("error interpreting forth: %d\n", rv); 756 exit(1); 757 } 758 rv = ficlVmEvaluate(bf_vm, "builtins definitions"); 759 if (rv != FICL_VM_STATUS_OUT_OF_TEXT) { 760 printf("error interpreting forth: %d\n", rv); 761 exit(1); 762 } 763 } 764 rv = ficlVmEvaluate(bf_vm, "only forth definitions"); 765 if (rv != FICL_VM_STATUS_OUT_OF_TEXT) { 766 printf("error interpreting forth: %d\n", rv); 767 exit(1); 768 } 769 770 /* 771 * Export some version numbers so that code can detect the 772 * loader/host version 773 */ 774 env = ficlSystemGetEnvironment(bf_sys); 775 (void) ficlDictionarySetConstant(env, "loader_version", 776 (bootprog_rev[0] - '0') * 10 + (bootprog_rev[2] - '0')); 777 778 /* try to load and run init file if present */ 779 if (rc == NULL) 780 rc = "/boot/forth/boot.4th"; 781 if (*rc != '\0') { 782 fd = open(rc, O_RDONLY); 783 if (fd != -1) { 784 (void) ficlExecFD(bf_vm, fd); 785 (void) close(fd); 786 } 787 } 788 789 gfx_framework_init(); 790 return (bf_vm); 791 } 792 793 void 794 bf_fini(void) 795 { 796 ficlSystemDestroy(bf_sys); 797 gfx_framework_fini(); 798 } 799 800 /* 801 * Feed a line of user input to the Forth interpreter 802 */ 803 int 804 bf_run(char *line) 805 { 806 int result; 807 ficlString s; 808 809 FICL_STRING_SET_FROM_CSTRING(s, line); 810 result = ficlVmExecuteString(bf_vm, s); 811 812 switch (result) { 813 case FICL_VM_STATUS_OUT_OF_TEXT: 814 case FICL_VM_STATUS_ABORTQ: 815 case FICL_VM_STATUS_QUIT: 816 case FICL_VM_STATUS_ERROR_EXIT: 817 break; 818 case FICL_VM_STATUS_USER_EXIT: 819 break; 820 case FICL_VM_STATUS_ABORT: 821 printf("Aborted!\n"); 822 break; 823 case BF_PARSE: 824 printf("Parse error!\n"); 825 break; 826 default: 827 if (command_errmsg != NULL) { 828 printf("%s\n", command_errmsg); 829 command_errmsg = NULL; 830 } 831 } 832 833 (void) setenv("interpret", bf_vm->state ? "" : "ok", 1); 834 835 return (result); 836 } 837 838 char * 839 get_dev(const char *path) 840 { 841 FILE *fp; 842 struct mnttab mpref = {0}; 843 struct mnttab mp = {0}; 844 char *currdev; 845 int ret; 846 char *buf; 847 char *tmppath; 848 char *tmpdev; 849 char *cwd = NULL; 850 851 fp = fopen(MNTTAB, "r"); 852 853 /* do the best we can to return something... */ 854 if (fp == NULL) 855 return (strdup(path)); 856 857 /* 858 * the path can have device provided, check for it 859 * and extract it. 860 */ 861 buf = strrchr(path, ':'); 862 if (buf != NULL) { 863 tmppath = buf+1; /* real path */ 864 buf = strchr(path, ':'); /* skip zfs: */ 865 buf++; 866 tmpdev = strdup(buf); 867 buf = strchr(tmpdev, ':'); /* get ending : */ 868 *buf = '\0'; 869 } else { 870 tmppath = (char *)path; 871 if (tmppath[0] != '/') 872 if ((cwd = getcwd(NULL, PATH_MAX)) == NULL) { 873 (void) fclose(fp); 874 return (strdup(path)); 875 } 876 877 currdev = getenv("currdev"); 878 buf = strchr(currdev, ':'); /* skip zfs: */ 879 if (buf == NULL) { 880 (void) fclose(fp); 881 return (strdup(path)); 882 } 883 buf++; 884 tmpdev = strdup(buf); 885 buf = strchr(tmpdev, ':'); /* get ending : */ 886 *buf = '\0'; 887 } 888 889 mpref.mnt_special = tmpdev; 890 ret = getmntany(fp, &mp, &mpref); 891 (void) fclose(fp); 892 free(tmpdev); 893 894 if (cwd == NULL) 895 (void) asprintf(&buf, "%s/%s", ret? "":mp.mnt_mountp, tmppath); 896 else { 897 (void) asprintf(&buf, "%s/%s/%s", ret? "":mp.mnt_mountp, cwd, 898 tmppath); 899 free(cwd); 900 } 901 return (buf); 902 } 903 904 static void 905 ngets(char *buf, int n) 906 { 907 int c; 908 char *lp; 909 910 for (lp = buf; ; ) 911 switch (c = getchar() & 0177) { 912 case '\n': 913 case '\r': 914 *lp = '\0'; 915 (void) putchar('\n'); 916 return; 917 case '\b': 918 case '\177': 919 if (lp > buf) { 920 lp--; 921 (void) putchar('\b'); 922 (void) putchar(' '); 923 (void) putchar('\b'); 924 } 925 break; 926 case 'r'&037: { 927 char *p; 928 929 (void) putchar('\n'); 930 for (p = buf; p < lp; ++p) 931 (void) putchar(*p); 932 break; 933 } 934 case 'u'&037: 935 case 'w'&037: 936 lp = buf; 937 (void) putchar('\n'); 938 break; 939 default: 940 if ((n < 1) || ((lp - buf) < n - 1)) { 941 *lp++ = c; 942 (void) putchar(c); 943 } 944 } 945 /*NOTREACHED*/ 946 } 947 948 static int 949 fgetstr(char *buf, int size, int fd) 950 { 951 char c; 952 int err, len; 953 954 size--; /* leave space for terminator */ 955 len = 0; 956 while (size != 0) { 957 err = read(fd, &c, sizeof (c)); 958 if (err < 0) /* read error */ 959 return (-1); 960 961 if (err == 0) { /* EOF */ 962 if (len == 0) 963 return (-1); /* nothing to read */ 964 break; 965 } 966 if ((c == '\r') || (c == '\n')) /* line terminators */ 967 break; 968 *buf++ = c; /* keep char */ 969 size--; 970 len++; 971 } 972 *buf = 0; 973 return (len); 974 } 975 976 static char * 977 unargv(int argc, char *argv[]) 978 { 979 size_t hlong; 980 int i; 981 char *cp; 982 983 for (i = 0, hlong = 0; i < argc; i++) 984 hlong += strlen(argv[i]) + 2; 985 986 if (hlong == 0) 987 return (NULL); 988 989 cp = malloc(hlong); 990 cp[0] = 0; 991 for (i = 0; i < argc; i++) { 992 (void) strcat(cp, argv[i]); 993 if (i < (argc - 1)) 994 (void) strcat(cp, " "); 995 } 996 997 return (cp); 998 } 999 1000 /* 1001 * Help is read from a formatted text file. 1002 * 1003 * Entries in the file are formatted as: 1004 * # Ttopic [Ssubtopic] Ddescription 1005 * help 1006 * text 1007 * here 1008 * # 1009 * 1010 * Note that for code simplicity's sake, the above format must be followed 1011 * exactly. 1012 * 1013 * Subtopic entries must immediately follow the topic (this is used to 1014 * produce the listing of subtopics). 1015 * 1016 * If no argument(s) are supplied by the user, the help for 'help' is displayed. 1017 */ 1018 static int 1019 help_getnext(int fd, char **topic, char **subtopic, char **desc) 1020 { 1021 char line[81], *cp, *ep; 1022 1023 *topic = *subtopic = *desc = NULL; 1024 for (;;) { 1025 if (fgetstr(line, 80, fd) < 0) 1026 return (0); 1027 1028 if (strlen(line) < 3 || line[0] != '#' || line[1] != ' ') 1029 continue; 1030 1031 *topic = *subtopic = *desc = NULL; 1032 cp = line + 2; 1033 while (cp != NULL && *cp != 0) { 1034 ep = strchr(cp, ' '); 1035 if (*cp == 'T' && *topic == NULL) { 1036 if (ep != NULL) 1037 *ep++ = 0; 1038 *topic = strdup(cp + 1); 1039 } else if (*cp == 'S' && *subtopic == NULL) { 1040 if (ep != NULL) 1041 *ep++ = 0; 1042 *subtopic = strdup(cp + 1); 1043 } else if (*cp == 'D') { 1044 *desc = strdup(cp + 1); 1045 ep = NULL; 1046 } 1047 cp = ep; 1048 } 1049 if (*topic == NULL) { 1050 free(*subtopic); 1051 free(*desc); 1052 continue; 1053 } 1054 return (1); 1055 } 1056 } 1057 1058 static int 1059 help_emitsummary(char *topic, char *subtopic, char *desc) 1060 { 1061 int i; 1062 1063 (void) pager_output(" "); 1064 (void) pager_output(topic); 1065 i = strlen(topic); 1066 if (subtopic != NULL) { 1067 (void) pager_output(" "); 1068 (void) pager_output(subtopic); 1069 i += strlen(subtopic) + 1; 1070 } 1071 if (desc != NULL) { 1072 do { 1073 (void) pager_output(" "); 1074 } while (i++ < 30); 1075 (void) pager_output(desc); 1076 } 1077 return (pager_output("\n")); 1078 } 1079 1080 COMMAND_SET(help, "help", "detailed help", command_help); 1081 1082 static int 1083 command_help(int argc, char *argv[]) 1084 { 1085 char buf[81]; /* XXX buffer size? */ 1086 int hfd, matched, doindex; 1087 char *topic, *subtopic, *t, *s, *d; 1088 1089 /* page the help text from our load path */ 1090 (void) snprintf(buf, sizeof (buf), "/boot/loader.help"); 1091 if ((hfd = open(buf, O_RDONLY)) < 0) { 1092 printf("Verbose help not available, " 1093 "use '?' to list commands\n"); 1094 return (CMD_OK); 1095 } 1096 1097 /* pick up request from arguments */ 1098 topic = subtopic = NULL; 1099 switch (argc) { 1100 case 3: 1101 subtopic = strdup(argv[2]); 1102 /* FALLTHROUGH */ 1103 case 2: 1104 topic = strdup(argv[1]); 1105 break; 1106 case 1: 1107 topic = strdup("help"); 1108 break; 1109 default: 1110 command_errmsg = "usage is 'help <topic> [<subtopic>]"; 1111 (void) close(hfd); 1112 return (CMD_ERROR); 1113 } 1114 1115 /* magic "index" keyword */ 1116 doindex = strcmp(topic, "index") == 0; 1117 matched = doindex; 1118 1119 /* Scan the helpfile looking for help matching the request */ 1120 pager_open(); 1121 while (help_getnext(hfd, &t, &s, &d)) { 1122 if (doindex) { /* dink around formatting */ 1123 if (help_emitsummary(t, s, d)) 1124 break; 1125 1126 } else if (strcmp(topic, t)) { 1127 /* topic mismatch */ 1128 /* nothing more on this topic, stop scanning */ 1129 if (matched) 1130 break; 1131 } else { 1132 /* topic matched */ 1133 matched = 1; 1134 if ((subtopic == NULL && s == NULL) || 1135 (subtopic != NULL && s != NULL && 1136 strcmp(subtopic, s) == 0)) { 1137 /* exact match, print text */ 1138 while (fgetstr(buf, 80, hfd) >= 0 && 1139 buf[0] != '#') { 1140 if (pager_output(buf)) 1141 break; 1142 if (pager_output("\n")) 1143 break; 1144 } 1145 } else if (subtopic == NULL && s != NULL) { 1146 /* topic match, list subtopics */ 1147 if (help_emitsummary(t, s, d)) 1148 break; 1149 } 1150 } 1151 free(t); 1152 free(s); 1153 free(d); 1154 t = s = d = NULL; 1155 } 1156 free(t); 1157 free(s); 1158 free(d); 1159 pager_close(); 1160 (void) close(hfd); 1161 if (!matched) { 1162 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1163 "no help available for '%s'", topic); 1164 free(topic); 1165 free(subtopic); 1166 return (CMD_ERROR); 1167 } 1168 free(topic); 1169 free(subtopic); 1170 return (CMD_OK); 1171 } 1172 1173 COMMAND_SET(commandlist, "?", "list commands", command_commandlist); 1174 1175 static int 1176 command_commandlist(int argc __unused, char *argv[] __unused) 1177 { 1178 struct bootblk_command **cmdp; 1179 int res; 1180 char name[20]; 1181 1182 res = 0; 1183 pager_open(); 1184 res = pager_output("Available commands:\n"); 1185 SET_FOREACH(cmdp, Xcommand_set) { 1186 if (res) 1187 break; 1188 if ((*cmdp)->c_name != NULL && (*cmdp)->c_desc != NULL) { 1189 (void) snprintf(name, sizeof (name), " %-15s ", 1190 (*cmdp)->c_name); 1191 (void) pager_output(name); 1192 (void) pager_output((*cmdp)->c_desc); 1193 res = pager_output("\n"); 1194 } 1195 } 1196 pager_close(); 1197 return (CMD_OK); 1198 } 1199 1200 /* 1201 * XXX set/show should become set/echo if we have variable 1202 * substitution happening. 1203 */ 1204 COMMAND_SET(show, "show", "show variable(s)", command_show); 1205 COMMAND_SET(printenv, "printenv", "show variable(s)", command_show); 1206 1207 static int 1208 command_show(int argc, char *argv[]) 1209 { 1210 char **ev; 1211 char *cp; 1212 1213 if (argc < 2) { 1214 /* 1215 * With no arguments, print everything. 1216 */ 1217 pager_open(); 1218 for (ev = _environ; *ev != NULL; ev++) { 1219 (void) pager_output(*ev); 1220 cp = getenv(*ev); 1221 if (cp != NULL) { 1222 (void) pager_output("="); 1223 (void) pager_output(cp); 1224 } 1225 if (pager_output("\n")) 1226 break; 1227 } 1228 pager_close(); 1229 } else { 1230 if ((cp = getenv(argv[1])) != NULL) { 1231 printf("%s\n", cp); 1232 } else { 1233 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1234 "variable '%s' not found", argv[1]); 1235 return (CMD_ERROR); 1236 } 1237 } 1238 return (CMD_OK); 1239 } 1240 1241 COMMAND_SET(set, "set", "set a variable", command_set); 1242 static int 1243 command_set(int argc, char *argv[]) 1244 { 1245 int err; 1246 char *value, *copy; 1247 1248 if (argc != 2) { 1249 command_errmsg = "wrong number of arguments"; 1250 return (CMD_ERROR); 1251 } else { 1252 copy = strdup(argv[1]); 1253 if (copy == NULL) { 1254 command_errmsg = strerror(errno); 1255 return (CMD_ERROR); 1256 } 1257 if ((value = strchr(copy, '=')) != NULL) 1258 *(value++) = 0; 1259 else 1260 value = ""; 1261 if ((err = setenv(copy, value, 1)) != 0) { 1262 free(copy); 1263 command_errmsg = strerror(errno); 1264 return (CMD_ERROR); 1265 } 1266 free(copy); 1267 } 1268 return (CMD_OK); 1269 } 1270 1271 COMMAND_SET(setprop, "setprop", "set a variable", command_setprop); 1272 static int 1273 command_setprop(int argc, char *argv[]) 1274 { 1275 int err; 1276 1277 if (argc != 3) { 1278 command_errmsg = "wrong number of arguments"; 1279 return (CMD_ERROR); 1280 } else { 1281 if ((err = setenv(argv[1], argv[2], 1)) != 0) { 1282 command_errmsg = strerror(err); 1283 return (CMD_ERROR); 1284 } 1285 } 1286 return (CMD_OK); 1287 } 1288 1289 COMMAND_SET(unset, "unset", "unset a variable", command_unset); 1290 static int 1291 command_unset(int argc, char *argv[]) 1292 { 1293 int err; 1294 1295 if (argc != 2) { 1296 command_errmsg = "wrong number of arguments"; 1297 return (CMD_ERROR); 1298 } else { 1299 if ((err = unsetenv(argv[1])) != 0) { 1300 command_errmsg = strerror(err); 1301 return (CMD_ERROR); 1302 } 1303 } 1304 return (CMD_OK); 1305 } 1306 1307 COMMAND_SET(echo, "echo", "echo arguments", command_echo); 1308 static int 1309 command_echo(int argc, char *argv[]) 1310 { 1311 char *s; 1312 int nl, ch; 1313 1314 nl = 0; 1315 optind = 1; 1316 opterr = 1; 1317 while ((ch = getopt(argc, argv, "n")) != -1) { 1318 switch (ch) { 1319 case 'n': 1320 nl = 1; 1321 break; 1322 case '?': 1323 default: 1324 /* getopt has already reported an error */ 1325 return (CMD_OK); 1326 } 1327 } 1328 argv += (optind); 1329 argc -= (optind); 1330 1331 s = unargv(argc, argv); 1332 if (s != NULL) { 1333 printf("%s", s); 1334 free(s); 1335 } 1336 if (!nl) 1337 printf("\n"); 1338 return (CMD_OK); 1339 } 1340 1341 /* 1342 * A passable emulation of the sh(1) command of the same name. 1343 */ 1344 static int 1345 ischar(void) 1346 { 1347 return (1); 1348 } 1349 1350 COMMAND_SET(read, "read", "read input from the terminal", command_read); 1351 static int 1352 command_read(int argc, char *argv[]) 1353 { 1354 char *prompt; 1355 int timeout; 1356 time_t when; 1357 char *cp; 1358 char *name; 1359 char buf[256]; /* XXX size? */ 1360 int c; 1361 1362 timeout = -1; 1363 prompt = NULL; 1364 optind = 1; 1365 opterr = 1; 1366 while ((c = getopt(argc, argv, "p:t:")) != -1) { 1367 switch (c) { 1368 case 'p': 1369 prompt = optarg; 1370 break; 1371 case 't': 1372 timeout = strtol(optarg, &cp, 0); 1373 if (cp == optarg) { 1374 (void) snprintf(command_errbuf, 1375 sizeof (command_errbuf), 1376 "bad timeout '%s'", optarg); 1377 return (CMD_ERROR); 1378 } 1379 break; 1380 default: 1381 return (CMD_OK); 1382 } 1383 } 1384 1385 argv += (optind); 1386 argc -= (optind); 1387 name = (argc > 0) ? argv[0]: NULL; 1388 1389 if (prompt != NULL) 1390 printf("%s", prompt); 1391 if (timeout >= 0) { 1392 when = time(NULL) + timeout; 1393 while (!ischar()) 1394 if (time(NULL) >= when) 1395 return (CMD_OK); /* is timeout an error? */ 1396 } 1397 1398 ngets(buf, sizeof (buf)); 1399 1400 if (name != NULL) 1401 (void) setenv(name, buf, 1); 1402 return (CMD_OK); 1403 } 1404 1405 /* 1406 * File pager 1407 */ 1408 COMMAND_SET(more, "more", "show contents of a file", command_more); 1409 static int 1410 command_more(int argc, char *argv[]) 1411 { 1412 int i; 1413 int res; 1414 char line[80]; 1415 char *name; 1416 1417 res = 0; 1418 pager_open(); 1419 for (i = 1; (i < argc) && (res == 0); i++) { 1420 (void) snprintf(line, sizeof (line), "*** FILE %s BEGIN ***\n", 1421 argv[i]); 1422 if (pager_output(line)) 1423 break; 1424 name = get_dev(argv[i]); 1425 res = page_file(name); 1426 free(name); 1427 if (!res) { 1428 (void) snprintf(line, sizeof (line), 1429 "*** FILE %s END ***\n", argv[i]); 1430 res = pager_output(line); 1431 } 1432 } 1433 pager_close(); 1434 1435 if (res == 0) 1436 return (CMD_OK); 1437 return (CMD_ERROR); 1438 } 1439 1440 static int 1441 page_file(char *filename) 1442 { 1443 int result; 1444 1445 result = pager_file(filename); 1446 1447 if (result == -1) { 1448 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1449 "error showing %s", filename); 1450 } 1451 1452 return (result); 1453 } 1454 1455 COMMAND_SET(ls, "ls", "list files", command_ls); 1456 static int 1457 command_ls(int argc, char *argv[]) 1458 { 1459 DIR *dir; 1460 int fd; 1461 struct stat sb; 1462 struct dirent *d; 1463 char *buf, *path; 1464 char lbuf[128]; /* one line */ 1465 int result, ch; 1466 int verbose; 1467 1468 result = CMD_OK; 1469 fd = -1; 1470 verbose = 0; 1471 optind = 1; 1472 opterr = 1; 1473 while ((ch = getopt(argc, argv, "l")) != -1) { 1474 switch (ch) { 1475 case 'l': 1476 verbose = 1; 1477 break; 1478 case '?': 1479 default: 1480 /* getopt has already reported an error */ 1481 return (CMD_OK); 1482 } 1483 } 1484 argv += (optind - 1); 1485 argc -= (optind - 1); 1486 1487 if (argc < 2) { 1488 path = ""; 1489 } else { 1490 path = argv[1]; 1491 } 1492 1493 fd = ls_getdir(&path); 1494 if (fd == -1) { 1495 result = CMD_ERROR; 1496 goto out; 1497 } 1498 dir = fdopendir(fd); 1499 pager_open(); 1500 (void) pager_output(path); 1501 (void) pager_output("\n"); 1502 1503 while ((d = readdir(dir)) != NULL) { 1504 if (strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) { 1505 /* stat the file, if possible */ 1506 if (path[0] == '\0') { 1507 (void) asprintf(&buf, "%s", d->d_name); 1508 } else { 1509 (void) asprintf(&buf, "%s/%s", path, d->d_name); 1510 } 1511 if (buf != NULL) { 1512 /* ignore return, could be symlink, etc. */ 1513 if (stat(buf, &sb)) { 1514 sb.st_size = 0; 1515 sb.st_mode = 0; 1516 } 1517 free(buf); 1518 } 1519 if (verbose) { 1520 (void) snprintf(lbuf, sizeof (lbuf), 1521 " %c %8d %s\n", 1522 typestr[sb.st_mode >> 12], 1523 (int)sb.st_size, d->d_name); 1524 } else { 1525 (void) snprintf(lbuf, sizeof (lbuf), 1526 " %c %s\n", 1527 typestr[sb.st_mode >> 12], d->d_name); 1528 } 1529 if (pager_output(lbuf)) 1530 goto out; 1531 } 1532 } 1533 out: 1534 pager_close(); 1535 if (fd != -1) 1536 (void) closedir(dir); 1537 if (path != NULL) 1538 free(path); 1539 return (result); 1540 } 1541 1542 /* 1543 * Given (path) containing a vaguely reasonable path specification, return an fd 1544 * on the directory, and an allocated copy of the path to the directory. 1545 */ 1546 static int 1547 ls_getdir(char **pathp) 1548 { 1549 struct stat sb; 1550 int fd; 1551 char *cp, *path; 1552 1553 fd = -1; 1554 1555 /* one extra byte for a possible trailing slash required */ 1556 path = malloc(strlen(*pathp) + 2); 1557 (void) strcpy(path, *pathp); 1558 1559 /* Make sure the path is respectable to begin with */ 1560 if ((cp = get_dev(path)) == NULL) { 1561 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1562 "bad path '%s'", path); 1563 goto out; 1564 } 1565 1566 /* If there's no path on the device, assume '/' */ 1567 if (*cp == 0) 1568 (void) strcat(path, "/"); 1569 1570 fd = open(cp, O_RDONLY); 1571 if (fd < 0) { 1572 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1573 "open '%s' failed: %s", path, strerror(errno)); 1574 goto out; 1575 } 1576 if (fstat(fd, &sb) < 0) { 1577 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1578 "stat failed: %s", strerror(errno)); 1579 goto out; 1580 } 1581 if (!S_ISDIR(sb.st_mode)) { 1582 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1583 "%s: %s", path, strerror(ENOTDIR)); 1584 goto out; 1585 } 1586 1587 free(cp); 1588 *pathp = path; 1589 return (fd); 1590 1591 out: 1592 free(cp); 1593 free(path); 1594 *pathp = NULL; 1595 if (fd != -1) 1596 (void) close(fd); 1597 return (-1); 1598 } 1599 1600 COMMAND_SET(include, "include", "read commands from a file", command_include); 1601 static int 1602 command_include(int argc, char *argv[]) 1603 { 1604 int i; 1605 int res; 1606 char **argvbuf; 1607 1608 /* 1609 * Since argv is static, we need to save it here. 1610 */ 1611 argvbuf = (char **)calloc(argc, sizeof (char *)); 1612 for (i = 0; i < argc; i++) 1613 argvbuf[i] = strdup(argv[i]); 1614 1615 res = CMD_OK; 1616 for (i = 1; (i < argc) && (res == CMD_OK); i++) 1617 res = include(argvbuf[i]); 1618 1619 for (i = 0; i < argc; i++) 1620 free(argvbuf[i]); 1621 free(argvbuf); 1622 1623 return (res); 1624 } 1625 1626 /* 1627 * Header prepended to each line. The text immediately follows the header. 1628 * We try to make this short in order to save memory -- the loader has 1629 * limited memory available, and some of the forth files are very long. 1630 */ 1631 struct includeline 1632 { 1633 struct includeline *next; 1634 int line; 1635 char text[]; 1636 }; 1637 1638 int 1639 include(const char *filename) 1640 { 1641 struct includeline *script, *se, *sp; 1642 int res = CMD_OK; 1643 int prevsrcid, fd, line; 1644 char *cp, input[256]; /* big enough? */ 1645 char *path; 1646 1647 path = get_dev(filename); 1648 if (((fd = open(path, O_RDONLY)) == -1)) { 1649 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1650 "can't open '%s': %s", filename, 1651 strerror(errno)); 1652 free(path); 1653 return (CMD_ERROR); 1654 } 1655 1656 free(path); 1657 /* 1658 * Read the script into memory. 1659 */ 1660 script = se = NULL; 1661 line = 0; 1662 1663 while (fgetstr(input, sizeof (input), fd) >= 0) { 1664 line++; 1665 cp = input; 1666 /* Allocate script line structure and copy line, flags */ 1667 if (*cp == '\0') 1668 continue; /* ignore empty line, save memory */ 1669 if (cp[0] == '\\' && cp[1] == ' ') 1670 continue; /* ignore comment */ 1671 1672 sp = malloc(sizeof (struct includeline) + strlen(cp) + 1); 1673 /* 1674 * On malloc failure (it happens!), free as much as possible 1675 * and exit 1676 */ 1677 if (sp == NULL) { 1678 while (script != NULL) { 1679 se = script; 1680 script = script->next; 1681 free(se); 1682 } 1683 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1684 "file '%s' line %d: memory allocation " 1685 "failure - aborting", filename, line); 1686 return (CMD_ERROR); 1687 } 1688 (void) strcpy(sp->text, cp); 1689 sp->line = line; 1690 sp->next = NULL; 1691 1692 if (script == NULL) { 1693 script = sp; 1694 } else { 1695 se->next = sp; 1696 } 1697 se = sp; 1698 } 1699 (void) close(fd); 1700 1701 /* 1702 * Execute the script 1703 */ 1704 1705 prevsrcid = bf_vm->sourceId.i; 1706 bf_vm->sourceId.i = fd+1; /* 0 is user input device */ 1707 1708 res = CMD_OK; 1709 1710 for (sp = script; sp != NULL; sp = sp->next) { 1711 res = bf_run(sp->text); 1712 if (res != FICL_VM_STATUS_OUT_OF_TEXT) { 1713 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1714 "Error while including %s, in the line %d:\n%s", 1715 filename, sp->line, sp->text); 1716 res = CMD_ERROR; 1717 break; 1718 } else 1719 res = CMD_OK; 1720 } 1721 1722 bf_vm->sourceId.i = -1; 1723 (void) bf_run(""); 1724 bf_vm->sourceId.i = prevsrcid; 1725 1726 while (script != NULL) { 1727 se = script; 1728 script = script->next; 1729 free(se); 1730 } 1731 1732 return (res); 1733 } 1734 1735 COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot); 1736 static int 1737 command_boot(int argc, char *argv[]) 1738 { 1739 return (CMD_OK); 1740 } 1741 1742 COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", 1743 command_autoboot); 1744 static int 1745 command_autoboot(int argc, char *argv[]) 1746 { 1747 return (CMD_OK); 1748 } 1749 1750 static void 1751 moduledir_rebuild(void) 1752 { 1753 struct moduledir *mdp, *mtmp; 1754 const char *path, *cp, *ep; 1755 int cplen; 1756 1757 path = getenv("module_path"); 1758 if (path == NULL) 1759 path = default_searchpath; 1760 /* 1761 * Rebuild list of module directories if it changed 1762 */ 1763 STAILQ_FOREACH(mdp, &moduledir_list, d_link) 1764 mdp->d_flags |= MDIR_REMOVED; 1765 1766 for (ep = path; *ep != 0; ep++) { 1767 cp = ep; 1768 for (; *ep != 0 && *ep != ';'; ep++) 1769 ; 1770 /* 1771 * Ignore trailing slashes 1772 */ 1773 for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; 1774 cplen--) 1775 ; 1776 STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 1777 if (strlen(mdp->d_path) != cplen || 1778 bcmp(cp, mdp->d_path, cplen) != 0) 1779 continue; 1780 mdp->d_flags &= ~MDIR_REMOVED; 1781 break; 1782 } 1783 if (mdp == NULL) { 1784 mdp = malloc(sizeof (*mdp) + cplen + 1); 1785 if (mdp == NULL) 1786 return; 1787 mdp->d_path = (char *)(mdp + 1); 1788 bcopy(cp, mdp->d_path, cplen); 1789 mdp->d_path[cplen] = 0; 1790 mdp->d_hints = NULL; 1791 mdp->d_flags = 0; 1792 STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); 1793 } 1794 if (*ep == 0) 1795 break; 1796 } 1797 /* 1798 * Delete unused directories if any 1799 */ 1800 mdp = STAILQ_FIRST(&moduledir_list); 1801 while (mdp) { 1802 if ((mdp->d_flags & MDIR_REMOVED) == 0) { 1803 mdp = STAILQ_NEXT(mdp, d_link); 1804 } else { 1805 if (mdp->d_hints) 1806 free(mdp->d_hints); 1807 mtmp = mdp; 1808 mdp = STAILQ_NEXT(mdp, d_link); 1809 STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); 1810 free(mtmp); 1811 } 1812 } 1813 } 1814 1815 static char * 1816 file_lookup(const char *path, const char *name, int namelen) 1817 { 1818 struct stat st; 1819 char *result, *cp, *gz; 1820 int pathlen; 1821 1822 pathlen = strlen(path); 1823 result = malloc(pathlen + namelen + 2); 1824 if (result == NULL) 1825 return (NULL); 1826 bcopy(path, result, pathlen); 1827 if (pathlen > 0 && result[pathlen - 1] != '/') 1828 result[pathlen++] = '/'; 1829 cp = result + pathlen; 1830 bcopy(name, cp, namelen); 1831 cp += namelen; 1832 *cp = '\0'; 1833 if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) 1834 return (result); 1835 /* also check for gz file */ 1836 (void) asprintf(&gz, "%s.gz", result); 1837 if (gz != NULL) { 1838 int res = stat(gz, &st); 1839 free(gz); 1840 if (res == 0) 1841 return (result); 1842 } 1843 free(result); 1844 return (NULL); 1845 } 1846 1847 static char * 1848 file_search(const char *name) 1849 { 1850 struct moduledir *mdp; 1851 struct stat sb; 1852 char *result; 1853 int namelen; 1854 1855 if (name == NULL) 1856 return (NULL); 1857 if (*name == 0) 1858 return (strdup(name)); 1859 1860 if (strchr(name, '/') != NULL) { 1861 char *gz; 1862 if (stat(name, &sb) == 0) 1863 return (strdup(name)); 1864 /* also check for gz file */ 1865 (void) asprintf(&gz, "%s.gz", name); 1866 if (gz != NULL) { 1867 int res = stat(gz, &sb); 1868 free(gz); 1869 if (res == 0) 1870 return (strdup(name)); 1871 } 1872 return (NULL); 1873 } 1874 1875 moduledir_rebuild(); 1876 result = NULL; 1877 namelen = strlen(name); 1878 STAILQ_FOREACH(mdp, &moduledir_list, d_link) { 1879 result = file_lookup(mdp->d_path, name, namelen); 1880 if (result) 1881 break; 1882 } 1883 return (result); 1884 } 1885 1886 COMMAND_SET(load, "load", "load a kernel or module", command_load); 1887 static int 1888 command_load(int argc, char *argv[]) 1889 { 1890 int dofile, ch; 1891 char *typestr = NULL; 1892 char *filename; 1893 dofile = 0; 1894 optind = 1; 1895 1896 if (argc == 1) { 1897 command_errmsg = "no filename specified"; 1898 return (CMD_ERROR); 1899 } 1900 1901 while ((ch = getopt(argc, argv, "kt:")) != -1) { 1902 switch (ch) { 1903 case 'k': 1904 break; 1905 case 't': 1906 typestr = optarg; 1907 dofile = 1; 1908 break; 1909 case '?': 1910 default: 1911 return (CMD_OK); 1912 } 1913 } 1914 argv += (optind - 1); 1915 argc -= (optind - 1); 1916 if (dofile) { 1917 if ((typestr == NULL) || (*typestr == 0)) { 1918 command_errmsg = "invalid load type"; 1919 return (CMD_ERROR); 1920 } 1921 #if 0 1922 return (file_loadraw(argv[1], typestr, argc - 2, argv + 2, 1) 1923 ? CMD_OK : CMD_ERROR); 1924 #endif 1925 return (CMD_OK); 1926 } 1927 1928 filename = file_search(argv[1]); 1929 if (filename == NULL) { 1930 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1931 "can't find '%s'", argv[1]); 1932 return (CMD_ERROR); 1933 } 1934 (void) setenv("kernelname", filename, 1); 1935 1936 return (CMD_OK); 1937 } 1938 1939 COMMAND_SET(unload, "unload", "unload all modules", command_unload); 1940 static int 1941 command_unload(int argc, char *argv[]) 1942 { 1943 (void) unsetenv("kernelname"); 1944 return (CMD_OK); 1945 } 1946 1947 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); 1948 static int 1949 command_reboot(int argc, char *argv[]) 1950 { 1951 exit(0); 1952 return (CMD_OK); 1953 } 1954 1955 COMMAND_SET(sifting, "sifting", "find words", command_sifting); 1956 static int 1957 command_sifting(int argc, char *argv[]) 1958 { 1959 if (argc != 2) { 1960 command_errmsg = "wrong number of arguments"; 1961 return (CMD_ERROR); 1962 } 1963 ficlPrimitiveSiftingImpl(bf_vm, argv[1]); 1964 return (CMD_OK); 1965 } 1966 1967 /* Only implement get and list. Ignore arguments on, off and set. */ 1968 COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management", 1969 command_framebuffer); 1970 static int 1971 command_framebuffer(int argc, char *argv[]) 1972 { 1973 if (fb.fd < 0) { 1974 printf("Framebuffer is not available.\n"); 1975 return (CMD_OK); 1976 } 1977 1978 if (argc == 2 && strcmp(argv[1], "get") == 0) { 1979 printf("\nSystem frame buffer: %s\n", fb.ident.name); 1980 printf("%dx%dx%d, stride=%d\n", fb.fb_width, fb.fb_height, 1981 fb.fb_depth, (fb.fb_pitch << 3) / fb.fb_depth); 1982 return (CMD_OK); 1983 } 1984 if (argc == 2 && strcmp(argv[1], "list") == 0) { 1985 printf("0: %dx%dx%d\n", fb.fb_width, fb.fb_height, fb.fb_depth); 1986 return (CMD_OK); 1987 } 1988 if (argc == 3 && strcmp(argv[1], "set") == 0) 1989 return (CMD_OK); 1990 if (argc == 2 && strcmp(argv[1], "on") == 0) 1991 return (CMD_OK); 1992 if (argc == 2 && strcmp(argv[1], "off") == 0) 1993 return (CMD_OK); 1994 1995 (void) snprintf(command_errbuf, sizeof (command_errbuf), 1996 "usage: %s get | list", argv[0]); 1997 return (CMD_ERROR); 1998 } 1999