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 pager_output(" "); 112 pager_output(topic); 113 i = strlen(topic); 114 if (subtopic != NULL) { 115 pager_output(" "); 116 pager_output(subtopic); 117 i += strlen(subtopic) + 1; 118 } 119 if (desc != NULL) { 120 do { 121 pager_output(" "); 122 } while (i++ < 30); 123 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 snprintf(buf, sizeof (buf), "%s/boot/loader.help", getenv("loaddev")); 138 if ((hfd = open(buf, O_RDONLY)) < 0) { 139 printf("Verbose help not available, " 140 "use '?' to list commands\n"); 141 return (CMD_OK); 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 close(hfd); 159 return (CMD_ERROR); 160 } 161 162 /* magic "index" keyword */ 163 doindex = strcmp(topic, "index") == 0? 1 : 0; 164 matched = doindex; 165 166 /* Scan the helpfile looking for help matching the request */ 167 pager_open(); 168 while (help_getnext(hfd, &t, &s, &d)) { 169 170 if (doindex) { /* dink around formatting */ 171 if (help_emitsummary(t, s, d)) 172 break; 173 174 } else if (strcmp(topic, t)) { 175 /* topic mismatch */ 176 if (matched) { 177 /* nothing more on this topic, stop scanning */ 178 break; 179 } 180 } else { 181 /* topic matched */ 182 matched = 1; 183 if ((subtopic == NULL && s == NULL) || 184 (subtopic != NULL && s != NULL && 185 strcmp(subtopic, s) == 0)) { 186 /* exact match, print text */ 187 while (fgetstr(buf, 80, hfd) >= 0 && 188 buf[0] != '#') { 189 if (pager_output(buf)) 190 break; 191 if (pager_output("\n")) 192 break; 193 } 194 } else if (subtopic == NULL && s != NULL) { 195 /* topic match, list subtopics */ 196 if (help_emitsummary(t, s, d)) 197 break; 198 } 199 } 200 free(t); 201 free(s); 202 free(d); 203 t = s = d = NULL; 204 } 205 free(t); 206 free(s); 207 free(d); 208 pager_close(); 209 close(hfd); 210 if (!matched) { 211 snprintf(command_errbuf, sizeof (command_errbuf), 212 "no help available for '%s'", topic); 213 free(topic); 214 free(subtopic); 215 return (CMD_ERROR); 216 } 217 free(topic); 218 free(subtopic); 219 return (CMD_OK); 220 } 221 222 COMMAND_SET(commandlist, "?", "list commands", command_commandlist); 223 224 static int 225 command_commandlist(int argc __unused, char *argv[] __unused) 226 { 227 struct bootblk_command **cmdp; 228 int res; 229 char name[20]; 230 231 res = 0; 232 pager_open(); 233 res = pager_output("Available commands:\n"); 234 SET_FOREACH(cmdp, Xcommand_set) { 235 if (res) 236 break; 237 if ((*cmdp)->c_name != NULL && (*cmdp)->c_desc != NULL) { 238 snprintf(name, sizeof (name)," %-15s ", 239 (*cmdp)->c_name); 240 pager_output(name); 241 pager_output((*cmdp)->c_desc); 242 res = pager_output("\n"); 243 } 244 } 245 pager_close(); 246 return (CMD_OK); 247 } 248 249 /* 250 * XXX set/show should become set/echo if we have variable 251 * substitution happening. 252 */ 253 254 COMMAND_SET(show, "show", "show variable(s)", command_show); 255 256 static int 257 command_show(int argc, char *argv[]) 258 { 259 struct env_var *ev; 260 char *cp; 261 262 if (argc < 2) { 263 /* 264 * With no arguments, print everything. 265 */ 266 pager_open(); 267 for (ev = environ; ev != NULL; ev = ev->ev_next) { 268 pager_output(ev->ev_name); 269 cp = getenv(ev->ev_name); 270 if (cp != NULL) { 271 pager_output("="); 272 pager_output(cp); 273 } 274 if (pager_output("\n")) 275 break; 276 } 277 pager_close(); 278 } else { 279 if ((cp = getenv(argv[1])) != NULL) { 280 printf("%s\n", cp); 281 } else { 282 snprintf(command_errbuf, sizeof (command_errbuf), 283 "variable '%s' not found", argv[1]); 284 return (CMD_ERROR); 285 } 286 } 287 return (CMD_OK); 288 } 289 290 COMMAND_SET(set, "set", "set a variable", command_set); 291 292 static int 293 command_set(int argc, char *argv[]) 294 { 295 int err; 296 297 if (argc != 2) { 298 command_errmsg = "wrong number of arguments"; 299 return (CMD_ERROR); 300 } else { 301 if ((err = putenv(argv[1])) != 0) { 302 command_errmsg = strerror(err); 303 return (CMD_ERROR); 304 } 305 } 306 return (CMD_OK); 307 } 308 309 COMMAND_SET(setprop, "setprop", "set a variable", command_setprop); 310 311 static int 312 command_setprop(int argc, char *argv[]) 313 { 314 int err; 315 316 if (argc != 3) { 317 command_errmsg = "wrong number of arguments"; 318 return (CMD_ERROR); 319 } else { 320 if ((err = setenv(argv[1], argv[2], 1)) != 0) { 321 command_errmsg = strerror(err); 322 return (CMD_ERROR); 323 } 324 } 325 return (CMD_OK); 326 } 327 328 COMMAND_SET(unset, "unset", "unset a variable", command_unset); 329 330 static int 331 command_unset(int argc, char *argv[]) 332 { 333 int err; 334 335 if (argc != 2) { 336 command_errmsg = "wrong number of arguments"; 337 return (CMD_ERROR); 338 } else { 339 if ((err = unsetenv(argv[1])) != 0) { 340 command_errmsg = strerror(err); 341 return (CMD_ERROR); 342 } 343 } 344 return (CMD_OK); 345 } 346 347 COMMAND_SET(echo, "echo", "echo arguments", command_echo); 348 349 static int 350 command_echo(int argc, char *argv[]) 351 { 352 char *s; 353 int nl, ch; 354 355 nl = 0; 356 optind = 1; 357 optreset = 1; 358 while ((ch = getopt(argc, argv, "n")) != -1) { 359 switch (ch) { 360 case 'n': 361 nl = 1; 362 break; 363 case '?': 364 default: 365 /* getopt has already reported an error */ 366 return (CMD_OK); 367 } 368 } 369 argv += (optind); 370 argc -= (optind); 371 372 s = unargv(argc, argv); 373 if (s != NULL) { 374 printf("%s", s); 375 free(s); 376 } 377 if (!nl) 378 printf("\n"); 379 return (CMD_OK); 380 } 381 382 /* 383 * A passable emulation of the sh(1) command of the same name. 384 */ 385 386 COMMAND_SET(read, "read", "read input from the terminal", command_read); 387 388 static int 389 command_read(int argc, char *argv[]) 390 { 391 char *prompt; 392 int timeout; 393 time_t when; 394 char *cp; 395 char *name; 396 char buf[256]; /* XXX size? */ 397 int c; 398 399 timeout = -1; 400 prompt = NULL; 401 optind = 1; 402 optreset = 1; 403 while ((c = getopt(argc, argv, "p:t:")) != -1) { 404 switch (c) { 405 case 'p': 406 prompt = optarg; 407 break; 408 case 't': 409 timeout = strtol(optarg, &cp, 0); 410 if (cp == optarg) { 411 snprintf(command_errbuf, 412 sizeof (command_errbuf), 413 "bad timeout '%s'", optarg); 414 return (CMD_ERROR); 415 } 416 break; 417 default: 418 return (CMD_OK); 419 } 420 } 421 422 argv += (optind); 423 argc -= (optind); 424 name = (argc > 0) ? argv[0]: NULL; 425 426 if (prompt != NULL) 427 printf("%s", prompt); 428 if (timeout >= 0) { 429 when = time(NULL) + timeout; 430 while (!ischar()) 431 if (time(NULL) >= when) 432 return (CMD_OK); /* is timeout an error? */ 433 } 434 435 ngets(buf, sizeof (buf)); 436 437 if (name != NULL) 438 setenv(name, buf, 1); 439 return (CMD_OK); 440 } 441 442 /* 443 * File pager 444 */ 445 COMMAND_SET(more, "more", "show contents of a file", command_more); 446 447 static int 448 command_more(int argc, char *argv[]) 449 { 450 int i; 451 int res; 452 char line[80]; 453 454 res = 0; 455 pager_open(); 456 for (i = 1; (i < argc) && (res == 0); i++) { 457 snprintf(line, sizeof (line), "*** FILE %s BEGIN ***\n", 458 argv[i]); 459 if (pager_output(line)) 460 break; 461 res = page_file(argv[i]); 462 if (!res) { 463 snprintf(line, sizeof (line), "*** FILE %s END ***\n", 464 argv[i]); 465 res = pager_output(line); 466 } 467 } 468 pager_close(); 469 470 if (res == 0) 471 return (CMD_OK); 472 else 473 return (CMD_ERROR); 474 } 475 476 static int 477 page_file(char *filename) 478 { 479 int result; 480 481 result = pager_file(filename); 482 483 if (result == -1) { 484 snprintf(command_errbuf, sizeof (command_errbuf), 485 "error showing %s", filename); 486 } 487 488 return (result); 489 } 490 491 /* 492 * List all disk-like devices 493 */ 494 COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); 495 496 static int 497 command_lsdev(int argc, char *argv[]) 498 { 499 int verbose, ch, i; 500 char line[80]; 501 502 verbose = 0; 503 optind = 1; 504 optreset = 1; 505 while ((ch = getopt(argc, argv, "v")) != -1) { 506 switch (ch) { 507 case 'v': 508 verbose = 1; 509 break; 510 case '?': 511 default: 512 /* getopt has already reported an error */ 513 return (CMD_OK); 514 } 515 } 516 argv += (optind); 517 argc -= (optind); 518 519 pager_open(); 520 for (i = 0; devsw[i] != NULL; i++) { 521 if (devsw[i]->dv_print != NULL) { 522 if (devsw[i]->dv_print(verbose)) 523 break; 524 } else { 525 snprintf(line, sizeof (line), "%s: (unknown)\n", 526 devsw[i]->dv_name); 527 if (pager_output(line)) 528 break; 529 } 530 } 531 pager_close(); 532 return (CMD_OK); 533 } 534