1 /* $NetBSD: commands.c,v 1.5 2014/04/08 21:51:06 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 /* __FBSDID("$FreeBSD: src/sys/boot/common/commands.c,v 1.19 2003/08/25 23:30:41 obrien Exp $"); */ 31 32 #include <lib/libsa/stand.h> 33 #include <lib/libsa/loadfile.h> 34 #include <lib/libkern/libkern.h> 35 36 #include "bootstrap.h" 37 38 static char command_errbuf[256]; 39 40 int 41 command_seterr(const char *fmt, ...) 42 { 43 int len; 44 va_list ap; 45 va_start(ap, fmt); 46 len = vsnprintf(command_errbuf, sizeof(command_errbuf), fmt, ap); 47 va_end(ap); 48 return len; 49 } 50 51 const char * 52 command_geterr(void) 53 { 54 return command_errbuf; 55 } 56 57 static int page_file(char *filename); 58 59 /* 60 * Help is read from a formatted text file. 61 * 62 * Entries in the file are formatted as 63 64 # Ttopic [Ssubtopic] Ddescription 65 help 66 text 67 here 68 # 69 70 * 71 * Note that for code simplicity's sake, the above format must be followed 72 * exactly. 73 * 74 * Subtopic entries must immediately follow the topic (this is used to 75 * produce the listing of subtopics). 76 * 77 * If no argument(s) are supplied by the user, the help for 'help' is displayed. 78 */ 79 80 static int 81 help_getnext(int fd, char **topic, char **subtopic, char **desc) 82 { 83 char line[81], *cp, *ep; 84 85 for (;;) { 86 if (fgetstr(line, 80, fd) < 0) 87 return(0); 88 89 if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' ')) 90 continue; 91 92 *topic = *subtopic = *desc = NULL; 93 cp = line + 2; 94 while((cp != NULL) && (*cp != 0)) { 95 ep = strchr(cp, ' '); 96 if ((*cp == 'T') && (*topic == NULL)) { 97 if (ep != NULL) 98 *ep++ = 0; 99 *topic = strdup(cp + 1); 100 } else if ((*cp == 'S') && (*subtopic == NULL)) { 101 if (ep != NULL) 102 *ep++ = 0; 103 *subtopic = strdup(cp + 1); 104 } else if (*cp == 'D') { 105 *desc = strdup(cp + 1); 106 ep = NULL; 107 } 108 cp = ep; 109 } 110 if (*topic == NULL) { 111 if (*subtopic != NULL) 112 free(*subtopic); 113 if (*desc != NULL) 114 free(*desc); 115 continue; 116 } 117 return(1); 118 } 119 } 120 121 static int 122 help_emitsummary(char *topic, char *subtopic, char *desc) 123 { 124 int i; 125 126 pager_output(" "); 127 pager_output(topic); 128 i = strlen(topic); 129 if (subtopic != NULL) { 130 pager_output(" "); 131 pager_output(subtopic); 132 i += strlen(subtopic) + 1; 133 } 134 if (desc != NULL) { 135 do { 136 pager_output(" "); 137 } while (i++ < 30); 138 pager_output(desc); 139 } 140 return (pager_output("\n")); 141 } 142 143 144 int 145 command_help(int argc, char *argv[]) 146 { 147 char buf[81]; /* XXX buffer size? */ 148 int hfd, matched, doindex; 149 char *topic, *subtopic, *t, *s, *d; 150 151 /* page the help text from our load path */ 152 snprintf(buf, sizeof(buf), "%s/boot/loader.help", getenv("loaddev")); 153 if ((hfd = open(buf, O_RDONLY)) < 0) { 154 printf("Verbose help not available, use '?' to list commands\n"); 155 return(CMD_OK); 156 } 157 158 /* pick up request from arguments */ 159 topic = subtopic = NULL; 160 switch(argc) { 161 case 3: 162 subtopic = strdup(argv[2]); 163 case 2: 164 topic = strdup(argv[1]); 165 break; 166 case 1: 167 topic = strdup("help"); 168 break; 169 default: 170 command_seterr("usage is 'help <topic> [<subtopic>]"); 171 return(CMD_ERROR); 172 } 173 174 /* magic "index" keyword */ 175 doindex = !strcmp(topic, "index"); 176 matched = doindex; 177 178 /* Scan the helpfile looking for help matching the request */ 179 pager_open(); 180 while(help_getnext(hfd, &t, &s, &d)) { 181 182 if (doindex) { /* dink around formatting */ 183 if (help_emitsummary(t, s, d)) 184 break; 185 186 } else if (strcmp(topic, t)) { 187 /* topic mismatch */ 188 if(matched) /* nothing more on this topic, stop scanning */ 189 break; 190 191 } else { 192 /* topic matched */ 193 matched = 1; 194 if (((subtopic == NULL) && (s == NULL)) || 195 ((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) { 196 /* exact match, print text */ 197 while((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) { 198 if (pager_output(buf)) 199 break; 200 if (pager_output("\n")) 201 break; 202 } 203 } else if ((subtopic == NULL) && (s != NULL)) { 204 /* topic match, list subtopics */ 205 if (help_emitsummary(t, s, d)) 206 break; 207 } 208 } 209 free(t); 210 free(s); 211 free(d); 212 } 213 pager_close(); 214 close(hfd); 215 if (!matched) { 216 command_seterr("no help available for '%s'", topic); 217 free(topic); 218 if (subtopic) 219 free(subtopic); 220 return(CMD_ERROR); 221 } 222 free(topic); 223 if (subtopic) 224 free(subtopic); 225 return(CMD_OK); 226 } 227 228 229 int 230 command_commandlist(int argc, char *argv[]) 231 { 232 struct bootblk_command *cmdp; 233 int res; 234 char name[20]; 235 int i; 236 237 res = 0; 238 pager_open(); 239 res = pager_output("Available commands:\n"); 240 241 for (i = 0, cmdp = commands; (cmdp->c_name != NULL) && (cmdp->c_desc != NULL ); i++, cmdp = commands + i) { 242 if (res) 243 break; 244 if ((cmdp->c_name != NULL) && (cmdp->c_desc != NULL)) { 245 snprintf(name, sizeof(name), " %s ", cmdp->c_name); 246 pager_output(name); 247 pager_output(cmdp->c_desc); 248 res = pager_output("\n"); 249 } 250 } 251 pager_close(); 252 return(CMD_OK); 253 } 254 255 /* 256 * XXX set/show should become set/echo if we have variable 257 * substitution happening. 258 */ 259 260 int 261 command_show(int argc, char *argv[]) 262 { 263 struct env_var *ev; 264 char *cp; 265 266 if (argc < 2) { 267 /* 268 * With no arguments, print everything. 269 */ 270 pager_open(); 271 for (ev = environ; ev != NULL; ev = ev->ev_next) { 272 pager_output(ev->ev_name); 273 cp = getenv(ev->ev_name); 274 if (cp != NULL) { 275 pager_output("="); 276 pager_output(cp); 277 } 278 if (pager_output("\n")) 279 break; 280 } 281 pager_close(); 282 } else { 283 if ((cp = getenv(argv[1])) != NULL) { 284 printf("%s\n", cp); 285 } else { 286 command_seterr("variable '%s' not found", argv[1]); 287 return(CMD_ERROR); 288 } 289 } 290 return(CMD_OK); 291 } 292 293 294 int 295 command_set(int argc, char *argv[]) 296 { 297 int err; 298 299 if (argc != 2) { 300 command_seterr("wrong number of arguments"); 301 return(CMD_ERROR); 302 } else { 303 if ((err = putenv(argv[1])) != 0) { 304 command_seterr("%s", strerror(err)); 305 return(CMD_ERROR); 306 } 307 } 308 return(CMD_OK); 309 } 310 311 312 int 313 command_unset(int argc, char *argv[]) 314 { 315 int err; 316 317 if (argc != 2) { 318 command_seterr("wrong number of arguments"); 319 return(CMD_ERROR); 320 } else { 321 if ((err = unsetenv(argv[1])) != 0) { 322 command_seterr("%s", strerror(err)); 323 return(CMD_ERROR); 324 } 325 } 326 return(CMD_OK); 327 } 328 329 330 int 331 command_echo(int argc, char *argv[]) 332 { 333 char *s; 334 int nl, ch; 335 336 nl = 0; 337 optind = 1; 338 optreset = 1; 339 while ((ch = getopt(argc, argv, "n")) != -1) { 340 switch(ch) { 341 case 'n': 342 nl = 1; 343 break; 344 case '?': 345 default: 346 /* getopt has already reported an error */ 347 return(CMD_OK); 348 } 349 } 350 argv += (optind); 351 argc -= (optind); 352 353 s = unargv(argc, argv); 354 if (s != NULL) { 355 printf("%s", s); 356 free(s); 357 } 358 if (!nl) 359 printf("\n"); 360 return(CMD_OK); 361 } 362 363 /* 364 * A passable emulation of the sh(1) command of the same name. 365 */ 366 367 368 int 369 command_read(int argc, char *argv[]) 370 { 371 char *prompt; 372 int timeout; 373 time_t when; 374 char *cp; 375 char *name; 376 char buf[256]; /* XXX size? */ 377 int c; 378 379 timeout = -1; 380 prompt = NULL; 381 optind = 1; 382 optreset = 1; 383 while ((c = getopt(argc, argv, "p:t:")) != -1) { 384 switch(c) { 385 386 case 'p': 387 prompt = optarg; 388 break; 389 case 't': 390 timeout = strtol(optarg, &cp, 0); 391 if (cp == optarg) { 392 command_seterr("bad timeout '%s'", optarg); 393 return(CMD_ERROR); 394 } 395 break; 396 default: 397 return(CMD_OK); 398 } 399 } 400 401 argv += (optind); 402 argc -= (optind); 403 name = (argc > 0) ? argv[0]: NULL; 404 405 if (prompt != NULL) 406 printf("%s", prompt); 407 if (timeout >= 0) { 408 when = time(NULL) + timeout; 409 while (!ischar()) 410 if (time(NULL) >= when) 411 return(CMD_OK); /* is timeout an error? */ 412 } 413 414 ngets(buf, sizeof(buf)); 415 416 if (name != NULL) 417 setenv(name, buf, 1); 418 return(CMD_OK); 419 } 420 421 /* 422 * File pager 423 */ 424 425 int 426 command_more(int argc, char *argv[]) 427 { 428 int i; 429 int res; 430 char line[80]; 431 432 res=0; 433 pager_open(); 434 for (i = 1; (i < argc) && (res == 0); i++) { 435 snprintf(line, sizeof(line), "*** FILE %s BEGIN ***\n", argv[i]); 436 if (pager_output(line)) 437 break; 438 res = page_file(argv[i]); 439 if (!res) { 440 snprintf(line, sizeof(line), "*** FILE %s END ***\n", argv[i]); 441 res = pager_output(line); 442 } 443 } 444 pager_close(); 445 446 if (res == 0) 447 return CMD_OK; 448 else 449 return CMD_ERROR; 450 } 451 452 static int 453 page_file(char *filename) 454 { 455 int result; 456 457 result = pager_file(filename); 458 459 if (result == -1) 460 command_seterr("error showing %s", filename); 461 462 return result; 463 } 464 465 /* 466 * List all disk-like devices 467 */ 468 469 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 494 snprintf(line, sizeof(line), "Device Enumeration:\n"); 495 pager_output(line); 496 497 for (i = 0; i < ndevs; i++) { 498 snprintf(line, sizeof(line), "%s\n", devsw[i].dv_name); 499 if (pager_output(line)) 500 break; 501 } 502 503 pager_close(); 504 return(CMD_OK); 505 } 506 507