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