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