commands.c revision 300156
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__FBSDID("$FreeBSD: head/sys/boot/common/commands.c 300156 2016-05-18 15:57:13Z imp $"); 29 30#include <stand.h> 31#include <string.h> 32 33#include "bootstrap.h" 34 35char *command_errmsg; 36char command_errbuf[256]; /* XXX should have procedural interface for setting, size limit? */ 37 38static 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 46help 47text 48here 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 */ 60COMMAND_SET(help, "help", "detailed help", command_help); 61 62static int 63help_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 continue; 98 } 99 return(1); 100 } 101} 102 103static int 104help_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 return (pager_output("\n")); 123} 124 125 126static int 127command_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 if ((hfd = open(buf, O_RDONLY)) < 0) { 136 printf("Verbose help not available, use '?' to list commands\n"); 137 return(CMD_OK); 138 } 139 140 /* pick up request from arguments */ 141 topic = subtopic = NULL; 142 switch(argc) { 143 case 3: 144 subtopic = strdup(argv[2]); 145 case 2: 146 topic = strdup(argv[1]); 147 break; 148 case 1: 149 topic = strdup("help"); 150 break; 151 default: 152 command_errmsg = "usage is 'help <topic> [<subtopic>]"; 153 close(hfd); 154 return(CMD_ERROR); 155 } 156 157 /* magic "index" keyword */ 158 doindex = !strcmp(topic, "index"); 159 matched = doindex; 160 161 /* Scan the helpfile looking for help matching the request */ 162 pager_open(); 163 while(help_getnext(hfd, &t, &s, &d)) { 164 165 if (doindex) { /* dink around formatting */ 166 if (help_emitsummary(t, s, d)) 167 break; 168 169 } else if (strcmp(topic, t)) { 170 /* topic mismatch */ 171 if(matched) /* nothing more on this topic, stop scanning */ 172 break; 173 174 } else { 175 /* topic matched */ 176 matched = 1; 177 if (((subtopic == NULL) && (s == NULL)) || 178 ((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) { 179 /* exact match, print text */ 180 while((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) { 181 if (pager_output(buf)) 182 break; 183 if (pager_output("\n")) 184 break; 185 } 186 } else if ((subtopic == NULL) && (s != NULL)) { 187 /* topic match, list subtopics */ 188 if (help_emitsummary(t, s, d)) 189 break; 190 } 191 } 192 free(t); 193 free(s); 194 free(d); 195 } 196 pager_close(); 197 close(hfd); 198 if (!matched) { 199 sprintf(command_errbuf, "no help available for '%s'", topic); 200 free(topic); 201 if (subtopic) 202 free(subtopic); 203 return(CMD_ERROR); 204 } 205 free(topic); 206 if (subtopic) 207 free(subtopic); 208 return(CMD_OK); 209} 210 211 212COMMAND_SET(commandlist, "?", "list commands", command_commandlist); 213 214/* 215 * Please note: although we use the pager for the list of commands, 216 * this routine is called from the ? FORTH function which then 217 * unconditionally prints some commands. This will lead to anomalous 218 * behavior. There's no 'pager_output' binding to FORTH to allow 219 * things to work right, so I'm documenting the bug rather than 220 * fixing it. 221 */ 222static int 223command_commandlist(int argc, char *argv[]) 224{ 225 struct bootblk_command **cmdp; 226 int res; 227 char name[20]; 228 229 res = 0; 230 pager_open(); 231 res = pager_output("Available commands:\n"); 232 SET_FOREACH(cmdp, Xcommand_set) { 233 if (res) 234 break; 235 if (((*cmdp)->c_name != NULL) && ((*cmdp)->c_desc != NULL)) { 236 sprintf(name, " %-15s ", (*cmdp)->c_name); 237 pager_output(name); 238 pager_output((*cmdp)->c_desc); 239 res = pager_output("\n"); 240 } 241 } 242 pager_close(); 243 return(CMD_OK); 244} 245 246/* 247 * XXX set/show should become set/echo if we have variable 248 * substitution happening. 249 */ 250 251COMMAND_SET(show, "show", "show variable(s)", command_show); 252 253static int 254command_show(int argc, char *argv[]) 255{ 256 struct env_var *ev; 257 char *cp; 258 259 if (argc < 2) { 260 /* 261 * With no arguments, print everything. 262 */ 263 pager_open(); 264 for (ev = environ; ev != NULL; ev = ev->ev_next) { 265 pager_output(ev->ev_name); 266 cp = getenv(ev->ev_name); 267 if (cp != NULL) { 268 pager_output("="); 269 pager_output(cp); 270 } 271 if (pager_output("\n")) 272 break; 273 } 274 pager_close(); 275 } else { 276 if ((cp = getenv(argv[1])) != NULL) { 277 printf("%s\n", cp); 278 } else { 279 sprintf(command_errbuf, "variable '%s' not found", argv[1]); 280 return(CMD_ERROR); 281 } 282 } 283 return(CMD_OK); 284} 285 286COMMAND_SET(set, "set", "set a variable", command_set); 287 288static int 289command_set(int argc, char *argv[]) 290{ 291 int err; 292 293 if (argc != 2) { 294 command_errmsg = "wrong number of arguments"; 295 return(CMD_ERROR); 296 } else { 297 if ((err = putenv(argv[1])) != 0) { 298 command_errmsg = strerror(err); 299 return(CMD_ERROR); 300 } 301 } 302 return(CMD_OK); 303} 304 305COMMAND_SET(unset, "unset", "unset a variable", command_unset); 306 307static int 308command_unset(int argc, char *argv[]) 309{ 310 int err; 311 312 if (argc != 2) { 313 command_errmsg = "wrong number of arguments"; 314 return(CMD_ERROR); 315 } else { 316 if ((err = unsetenv(argv[1])) != 0) { 317 command_errmsg = strerror(err); 318 return(CMD_ERROR); 319 } 320 } 321 return(CMD_OK); 322} 323 324COMMAND_SET(echo, "echo", "echo arguments", command_echo); 325 326static int 327command_echo(int argc, char *argv[]) 328{ 329 char *s; 330 int nl, ch; 331 332 nl = 0; 333 optind = 1; 334 optreset = 1; 335 while ((ch = getopt(argc, argv, "n")) != -1) { 336 switch(ch) { 337 case 'n': 338 nl = 1; 339 break; 340 case '?': 341 default: 342 /* getopt has already reported an error */ 343 return(CMD_OK); 344 } 345 } 346 argv += (optind); 347 argc -= (optind); 348 349 s = unargv(argc, argv); 350 if (s != NULL) { 351 printf("%s", s); 352 free(s); 353 } 354 if (!nl) 355 printf("\n"); 356 return(CMD_OK); 357} 358 359/* 360 * A passable emulation of the sh(1) command of the same name. 361 */ 362 363COMMAND_SET(read, "read", "read input from the terminal", command_read); 364 365static int 366command_read(int argc, char *argv[]) 367{ 368 char *prompt; 369 int timeout; 370 time_t when; 371 char *cp; 372 char *name; 373 char buf[256]; /* XXX size? */ 374 int c; 375 376 timeout = -1; 377 prompt = NULL; 378 optind = 1; 379 optreset = 1; 380 while ((c = getopt(argc, argv, "p:t:")) != -1) { 381 switch(c) { 382 383 case 'p': 384 prompt = optarg; 385 break; 386 case 't': 387 timeout = strtol(optarg, &cp, 0); 388 if (cp == optarg) { 389 sprintf(command_errbuf, "bad timeout '%s'", optarg); 390 return(CMD_ERROR); 391 } 392 break; 393 default: 394 return(CMD_OK); 395 } 396 } 397 398 argv += (optind); 399 argc -= (optind); 400 name = (argc > 0) ? argv[0]: NULL; 401 402 if (prompt != NULL) 403 printf("%s", prompt); 404 if (timeout >= 0) { 405 when = time(NULL) + timeout; 406 while (!ischar()) 407 if (time(NULL) >= when) 408 return(CMD_OK); /* is timeout an error? */ 409 } 410 411 ngets(buf, sizeof(buf)); 412 413 if (name != NULL) 414 setenv(name, buf, 1); 415 return(CMD_OK); 416} 417 418/* 419 * File pager 420 */ 421COMMAND_SET(more, "more", "show contents of a file", command_more); 422 423static int 424command_more(int argc, char *argv[]) 425{ 426 int i; 427 int res; 428 char line[80]; 429 430 res=0; 431 pager_open(); 432 for (i = 1; (i < argc) && (res == 0); i++) { 433 sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]); 434 if (pager_output(line)) 435 break; 436 res = page_file(argv[i]); 437 if (!res) { 438 sprintf(line, "*** FILE %s END ***\n", argv[i]); 439 res = pager_output(line); 440 } 441 } 442 pager_close(); 443 444 if (res == 0) 445 return CMD_OK; 446 else 447 return CMD_ERROR; 448} 449 450static int 451page_file(char *filename) 452{ 453 int result; 454 455 result = pager_file(filename); 456 457 if (result == -1) 458 sprintf(command_errbuf, "error showing %s", filename); 459 460 return result; 461} 462 463/* 464 * List all disk-like devices 465 */ 466COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); 467 468static int 469command_lsdev(int argc, char *argv[]) 470{ 471 int verbose, ch, i; 472 char line[80]; 473 474 verbose = 0; 475 optind = 1; 476 optreset = 1; 477 while ((ch = getopt(argc, argv, "v")) != -1) { 478 switch(ch) { 479 case 'v': 480 verbose = 1; 481 break; 482 case '?': 483 default: 484 /* getopt has already reported an error */ 485 return(CMD_OK); 486 } 487 } 488 argv += (optind); 489 argc -= (optind); 490 491 pager_open(); 492 for (i = 0; devsw[i] != NULL; i++) { 493 if (devsw[i]->dv_print != NULL){ 494 sprintf(line, "%s devices:\n", devsw[i]->dv_name); 495 if (pager_output(line)) 496 break; 497 devsw[i]->dv_print(verbose); 498 } else { 499 sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name); 500 if (pager_output(line)) 501 break; 502 } 503 } 504 pager_close(); 505 return(CMD_OK); 506} 507 508