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 27 #include <sys/cdefs.h> 28 29 #include <stand.h> 30 #include <string.h> 31 32 #include "bootstrap.h" 33 34 const char *command_errmsg; 35 /* XXX should have procedural interface for setting, size limit? */ 36 char command_errbuf[COMMAND_ERRBUFSZ]; 37 38 static int page_file(char *filename); 39 40 /* BEGIN CSTYLED */ 41 /* 42 * Help is read from a formatted text file. 43 * 44 * Entries in the file are formatted as 45 46 # Ttopic [Ssubtopic] Ddescription 47 help 48 text 49 here 50 # 51 52 * 53 * Note that for code simplicity's sake, the above format must be followed 54 * exactly. 55 * 56 * Subtopic entries must immediately follow the topic (this is used to 57 * produce the listing of subtopics). 58 * 59 * If no argument(s) are supplied by the user, the help for 'help' is displayed. 60 */ 61 /* END CSTYLED */ 62 COMMAND_SET(help, "help", "detailed help", command_help); 63 64 static int 65 help_getnext(int fd, char **topic, char **subtopic, char **desc) 66 { 67 char line[81], *cp, *ep; 68 69 /* Make sure we provide sane values. */ 70 *topic = *subtopic = *desc = NULL; 71 for (;;) { 72 if (fgetstr(line, 80, fd) < 0) 73 return (0); 74 75 if (strlen(line) < 3 || line[0] != '#' || line[1] != ' ') 76 continue; 77 78 *topic = *subtopic = *desc = NULL; 79 cp = line + 2; 80 while (cp != NULL && *cp != 0) { 81 ep = strchr(cp, ' '); 82 if (*cp == 'T' && *topic == NULL) { 83 if (ep != NULL) 84 *ep++ = 0; 85 *topic = strdup(cp + 1); 86 } else if (*cp == 'S' && *subtopic == NULL) { 87 if (ep != NULL) 88 *ep++ = 0; 89 *subtopic = strdup(cp + 1); 90 } else if (*cp == 'D') { 91 *desc = strdup(cp + 1); 92 ep = NULL; 93 } 94 cp = ep; 95 } 96 if (*topic == NULL) { 97 free(*subtopic); 98 free(*desc); 99 *subtopic = *desc = NULL; 100 continue; 101 } 102 return (1); 103 } 104 } 105 106 static int 107 help_emitsummary(char *topic, char *subtopic, char *desc) 108 { 109 int i; 110 111 (void) pager_output(" "); 112 (void) pager_output(topic); 113 i = strlen(topic); 114 if (subtopic != NULL) { 115 (void) pager_output(" "); 116 (void) pager_output(subtopic); 117 i += strlen(subtopic) + 1; 118 } 119 if (desc != NULL) { 120 do { 121 (void) pager_output(" "); 122 } while (i++ < 30); 123 (void) pager_output(desc); 124 } 125 return (pager_output("\n")); 126 } 127 128 129 static int 130 command_help(int argc, char *argv[]) 131 { 132 char buf[81]; /* XXX buffer size? */ 133 int hfd, matched, doindex; 134 char *topic, *subtopic, *t, *s, *d; 135 136 /* page the help text from our load path */ 137 (void) snprintf(buf, sizeof (buf), "%s/boot/loader.help", 138 getenv("loaddev")); 139 if ((hfd = open(buf, O_RDONLY)) < 0) { 140 printf("Verbose help not available, " 141 "use '?' to list commands\n"); 142 return (CMD_OK); 143 } 144 145 /* pick up request from arguments */ 146 topic = subtopic = NULL; 147 switch (argc) { 148 case 3: 149 subtopic = strdup(argv[2]); 150 /* FALLTHROUGH */ 151 case 2: 152 topic = strdup(argv[1]); 153 break; 154 case 1: 155 topic = strdup("help"); 156 break; 157 default: 158 command_errmsg = "usage is 'help <topic> [<subtopic>]"; 159 (void) close(hfd); 160 return (CMD_ERROR); 161 } 162 163 /* magic "index" keyword */ 164 doindex = strcmp(topic, "index") == 0? 1 : 0; 165 matched = doindex; 166 167 /* Scan the helpfile looking for help matching the request */ 168 pager_open(); 169 while (help_getnext(hfd, &t, &s, &d)) { 170 171 if (doindex) { /* dink around formatting */ 172 if (help_emitsummary(t, s, d)) 173 break; 174 175 } else if (strcmp(topic, t)) { 176 /* topic mismatch */ 177 if (matched) { 178 /* nothing more on this topic, stop scanning */ 179 break; 180 } 181 } else { 182 /* topic matched */ 183 matched = 1; 184 if ((subtopic == NULL && s == NULL) || 185 (subtopic != NULL && s != NULL && 186 strcmp(subtopic, s) == 0)) { 187 /* exact match, print text */ 188 while (fgetstr(buf, 80, hfd) >= 0 && 189 buf[0] != '#') { 190 if (pager_output(buf)) 191 break; 192 if (pager_output("\n")) 193 break; 194 } 195 } else if (subtopic == NULL && s != NULL) { 196 /* topic match, list subtopics */ 197 if (help_emitsummary(t, s, d)) 198 break; 199 } 200 } 201 free(t); 202 free(s); 203 free(d); 204 t = s = d = NULL; 205 } 206 free(t); 207 free(s); 208 free(d); 209 pager_close(); 210 (void) close(hfd); 211 if (!matched) { 212 (void) snprintf(command_errbuf, sizeof (command_errbuf), 213 "no help available for '%s'", topic); 214 free(topic); 215 free(subtopic); 216 return (CMD_ERROR); 217 } 218 free(topic); 219 free(subtopic); 220 return (CMD_OK); 221 } 222 223 COMMAND_SET(commandlist, "?", "list commands", command_commandlist); 224 225 static int 226 command_commandlist(int argc __unused, char *argv[] __unused) 227 { 228 struct bootblk_command **cmdp; 229 int res; 230 char name[20]; 231 232 res = 0; 233 pager_open(); 234 res = pager_output("Available commands:\n"); 235 SET_FOREACH(cmdp, Xcommand_set) { 236 if (res) 237 break; 238 if ((*cmdp)->c_name != NULL && (*cmdp)->c_desc != NULL) { 239 (void) snprintf(name, sizeof (name), " %-15s ", 240 (*cmdp)->c_name); 241 (void) pager_output(name); 242 (void) pager_output((*cmdp)->c_desc); 243 res = pager_output("\n"); 244 } 245 } 246 pager_close(); 247 return (CMD_OK); 248 } 249 250 /* 251 * XXX set/show should become set/echo if we have variable 252 * substitution happening. 253 */ 254 255 COMMAND_SET(show, "show", "show variable(s)", command_show); 256 257 static int 258 command_show(int argc, char *argv[]) 259 { 260 struct env_var *ev; 261 char *cp; 262 263 if (argc < 2) { 264 /* 265 * With no arguments, print everything. 266 */ 267 pager_open(); 268 for (ev = environ; ev != NULL; ev = ev->ev_next) { 269 (void) pager_output(ev->ev_name); 270 cp = getenv(ev->ev_name); 271 if (cp != NULL) { 272 (void) pager_output("="); 273 (void) pager_output(cp); 274 } 275 if (pager_output("\n")) 276 break; 277 } 278 pager_close(); 279 } else { 280 if ((cp = getenv(argv[1])) != NULL) { 281 printf("%s\n", cp); 282 } else { 283 (void) snprintf(command_errbuf, sizeof (command_errbuf), 284 "variable '%s' not found", argv[1]); 285 return (CMD_ERROR); 286 } 287 } 288 return (CMD_OK); 289 } 290 291 COMMAND_SET(set, "set", "set a variable", command_set); 292 293 static int 294 command_set(int argc, char *argv[]) 295 { 296 int err; 297 298 if (argc != 2) { 299 command_errmsg = "wrong number of arguments"; 300 return (CMD_ERROR); 301 } else { 302 if ((err = putenv(argv[1])) != 0) { 303 command_errmsg = strerror(err); 304 return (CMD_ERROR); 305 } 306 } 307 return (CMD_OK); 308 } 309 310 COMMAND_SET(setprop, "setprop", "set a variable", command_setprop); 311 312 static int 313 command_setprop(int argc, char *argv[]) 314 { 315 int err; 316 317 if (argc != 3) { 318 command_errmsg = "wrong number of arguments"; 319 return (CMD_ERROR); 320 } else { 321 if ((err = setenv(argv[1], argv[2], 1)) != 0) { 322 command_errmsg = strerror(err); 323 return (CMD_ERROR); 324 } 325 } 326 return (CMD_OK); 327 } 328 329 COMMAND_SET(unset, "unset", "unset a variable", command_unset); 330 331 static int 332 command_unset(int argc, char *argv[]) 333 { 334 int err; 335 336 if (argc != 2) { 337 command_errmsg = "wrong number of arguments"; 338 return (CMD_ERROR); 339 } else { 340 if ((err = unsetenv(argv[1])) != 0) { 341 command_errmsg = strerror(err); 342 return (CMD_ERROR); 343 } 344 } 345 return (CMD_OK); 346 } 347 348 COMMAND_SET(echo, "echo", "echo arguments", command_echo); 349 350 static int 351 command_echo(int argc, char *argv[]) 352 { 353 char *s; 354 int nl, ch; 355 356 nl = 0; 357 optind = 1; 358 optreset = 1; 359 while ((ch = getopt(argc, argv, "n")) != -1) { 360 switch (ch) { 361 case 'n': 362 nl = 1; 363 break; 364 case '?': 365 default: 366 /* getopt has already reported an error */ 367 return (CMD_OK); 368 } 369 } 370 argv += (optind); 371 argc -= (optind); 372 373 s = unargv(argc, argv); 374 if (s != NULL) { 375 printf("%s", s); 376 free(s); 377 } 378 if (!nl) 379 printf("\n"); 380 return (CMD_OK); 381 } 382 383 /* 384 * A passable emulation of the sh(1) command of the same name. 385 */ 386 387 COMMAND_SET(read, "read", "read input from the terminal", command_read); 388 389 static int 390 command_read(int argc, char *argv[]) 391 { 392 char *prompt; 393 int timeout; 394 time_t when; 395 char *cp; 396 char *name; 397 char buf[256]; /* XXX size? */ 398 int c; 399 400 timeout = -1; 401 prompt = NULL; 402 optind = 1; 403 optreset = 1; 404 while ((c = getopt(argc, argv, "p:t:")) != -1) { 405 switch (c) { 406 case 'p': 407 prompt = optarg; 408 break; 409 case 't': 410 timeout = strtol(optarg, &cp, 0); 411 if (cp == optarg) { 412 (void) snprintf(command_errbuf, 413 sizeof (command_errbuf), 414 "bad timeout '%s'", optarg); 415 return (CMD_ERROR); 416 } 417 break; 418 default: 419 return (CMD_OK); 420 } 421 } 422 423 argv += (optind); 424 argc -= (optind); 425 name = (argc > 0) ? argv[0]: NULL; 426 427 if (prompt != NULL) 428 printf("%s", prompt); 429 if (timeout >= 0) { 430 when = time(NULL) + timeout; 431 while (!ischar()) 432 if (time(NULL) >= when) 433 return (CMD_OK); /* is timeout an error? */ 434 } 435 436 ngets(buf, sizeof (buf)); 437 438 if (name != NULL) 439 (void) setenv(name, buf, 1); 440 return (CMD_OK); 441 } 442 443 /* 444 * File pager 445 */ 446 COMMAND_SET(more, "more", "show contents of a file", command_more); 447 448 static int 449 command_more(int argc, char *argv[]) 450 { 451 int i; 452 int res; 453 char line[80]; 454 455 res = 0; 456 pager_open(); 457 for (i = 1; (i < argc) && (res == 0); i++) { 458 (void) snprintf(line, sizeof (line), "*** FILE %s BEGIN ***\n", 459 argv[i]); 460 if (pager_output(line)) 461 break; 462 res = page_file(argv[i]); 463 if (!res) { 464 (void) snprintf(line, sizeof (line), 465 "*** FILE %s END ***\n", argv[i]); 466 res = pager_output(line); 467 } 468 } 469 pager_close(); 470 471 if (res == 0) 472 return (CMD_OK); 473 else 474 return (CMD_ERROR); 475 } 476 477 static int 478 page_file(char *filename) 479 { 480 int result; 481 482 result = pager_file(filename); 483 484 if (result == -1) { 485 (void) snprintf(command_errbuf, sizeof (command_errbuf), 486 "error showing %s", filename); 487 } 488 489 return (result); 490 } 491 492 /* 493 * List all disk-like devices 494 */ 495 COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); 496 497 static int 498 command_lsdev(int argc, char *argv[]) 499 { 500 int verbose, ch, i; 501 char line[80]; 502 503 verbose = 0; 504 optind = 1; 505 optreset = 1; 506 while ((ch = getopt(argc, argv, "v")) != -1) { 507 switch (ch) { 508 case 'v': 509 verbose = 1; 510 break; 511 case '?': 512 default: 513 /* getopt has already reported an error */ 514 return (CMD_OK); 515 } 516 } 517 argv += (optind); 518 argc -= (optind); 519 520 pager_open(); 521 for (i = 0; devsw[i] != NULL; i++) { 522 if (devsw[i]->dv_print != NULL) { 523 if (devsw[i]->dv_print(verbose)) 524 break; 525 } else { 526 (void) snprintf(line, sizeof (line), "%s: (unknown)\n", 527 devsw[i]->dv_name); 528 if (pager_output(line)) 529 break; 530 } 531 } 532 pager_close(); 533 return (CMD_OK); 534 } 535