138465Smsmith/*- 238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 338465Smsmith * All rights reserved. 438465Smsmith * 538465Smsmith * Redistribution and use in source and binary forms, with or without 638465Smsmith * modification, are permitted provided that the following conditions 738465Smsmith * are met: 838465Smsmith * 1. Redistributions of source code must retain the above copyright 938465Smsmith * notice, this list of conditions and the following disclaimer. 1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138465Smsmith * notice, this list of conditions and the following disclaimer in the 1238465Smsmith * documentation and/or other materials provided with the distribution. 1338465Smsmith * 1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738465Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438465Smsmith * SUCH DAMAGE. 2538465Smsmith */ 2638465Smsmith 27119483Sobrien#include <sys/cdefs.h> 28119483Sobrien__FBSDID("$FreeBSD: stable/11/stand/common/commands.c 344286 2019-02-19 18:34:00Z kevans $"); 29119483Sobrien 3038465Smsmith#include <stand.h> 3138465Smsmith#include <string.h> 3238465Smsmith 3338465Smsmith#include "bootstrap.h" 3438465Smsmith 35344286Skevansconst char *command_errmsg; 36329010Skevans/* XXX should have procedural interface for setting, size limit? */ 37329010Skevanschar command_errbuf[COMMAND_ERRBUFSZ]; 3840775Smsmith 3943491Sjkhstatic int page_file(char *filename); 4040775Smsmith 4140775Smsmith/* 4240775Smsmith * Help is read from a formatted text file. 4340775Smsmith * 4440775Smsmith * Entries in the file are formatted as 4540775Smsmith 4640775Smsmith# Ttopic [Ssubtopic] Ddescription 4740775Smsmithhelp 4840775Smsmithtext 4940775Smsmithhere 5040775Smsmith# 5140775Smsmith 5240775Smsmith * 5340775Smsmith * Note that for code simplicity's sake, the above format must be followed 5440775Smsmith * exactly. 5540775Smsmith * 5640775Smsmith * Subtopic entries must immediately follow the topic (this is used to 5740775Smsmith * produce the listing of subtopics). 5840775Smsmith * 5940775Smsmith * If no argument(s) are supplied by the user, the help for 'help' is displayed. 6040775Smsmith */ 6138465SmsmithCOMMAND_SET(help, "help", "detailed help", command_help); 6238465Smsmith 6338465Smsmithstatic int 64344286Skevanshelp_getnext(int fd, char **topic, char **subtopic, char **desc) 6538465Smsmith{ 66344286Skevans char line[81], *cp, *ep; 67344285Skevans 68344286Skevans /* Make sure we provide sane values. */ 69344286Skevans *topic = *subtopic = *desc = NULL; 70344286Skevans for (;;) { 71344286Skevans if (fgetstr(line, 80, fd) < 0) 72344286Skevans return (0); 7338465Smsmith 74344286Skevans if (strlen(line) < 3 || line[0] != '#' || line[1] != ' ') 75344286Skevans continue; 76344286Skevans 77344286Skevans cp = line + 2; 78344286Skevans while (cp != NULL && *cp != 0) { 79344286Skevans ep = strchr(cp, ' '); 80344286Skevans if (*cp == 'T' && *topic == NULL) { 81344286Skevans if (ep != NULL) 82344286Skevans *ep++ = 0; 83344286Skevans *topic = strdup(cp + 1); 84344286Skevans } else if (*cp == 'S' && *subtopic == NULL) { 85344286Skevans if (ep != NULL) 86344286Skevans *ep++ = 0; 87344286Skevans *subtopic = strdup(cp + 1); 88344286Skevans } else if (*cp == 'D') { 89344286Skevans *desc = strdup(cp + 1); 90344286Skevans ep = NULL; 91344286Skevans } 92344286Skevans cp = ep; 93344286Skevans } 94344286Skevans if (*topic == NULL) { 95344286Skevans free(*subtopic); 96344286Skevans free(*desc); 97344286Skevans *subtopic = *desc = NULL; 98344286Skevans continue; 99344286Skevans } 100344286Skevans return (1); 10140775Smsmith } 10240775Smsmith} 10340775Smsmith 104135929Srustatic int 10540775Smsmithhelp_emitsummary(char *topic, char *subtopic, char *desc) 10640775Smsmith{ 107344286Skevans int i; 108344286Skevans 109344286Skevans pager_output(" "); 110344286Skevans pager_output(topic); 111344286Skevans i = strlen(topic); 112344286Skevans if (subtopic != NULL) { 113344286Skevans pager_output(" "); 114344286Skevans pager_output(subtopic); 115344286Skevans i += strlen(subtopic) + 1; 116344286Skevans } 117344286Skevans if (desc != NULL) { 118344286Skevans do { 119344286Skevans pager_output(" "); 120344286Skevans } while (i++ < 30); 121344286Skevans pager_output(desc); 122344286Skevans } 123344286Skevans return (pager_output("\n")); 12440775Smsmith} 12540775Smsmith 126344286Skevans 12740775Smsmithstatic int 128344286Skevanscommand_help(int argc, char *argv[]) 12940775Smsmith{ 130344286Skevans char buf[81]; /* XXX buffer size? */ 131344286Skevans int hfd, matched, doindex; 132344286Skevans char *topic, *subtopic, *t, *s, *d; 13340775Smsmith 134344286Skevans /* page the help text from our load path */ 135344286Skevans snprintf(buf, sizeof(buf), "%s/boot/loader.help", getenv("loaddev")); 136344286Skevans if ((hfd = open(buf, O_RDONLY)) < 0) { 137344286Skevans printf("Verbose help not available, " 138344286Skevans "use '?' to list commands\n"); 139344286Skevans return (CMD_OK); 140344286Skevans } 14140775Smsmith 142344286Skevans /* pick up request from arguments */ 143344286Skevans topic = subtopic = NULL; 144344286Skevans switch (argc) { 145344286Skevans case 3: 146344286Skevans subtopic = strdup(argv[2]); 147344286Skevans /* FALLTHROUGH */ 148344286Skevans case 2: 149344286Skevans topic = strdup(argv[1]); 150344286Skevans break; 151344286Skevans case 1: 152344286Skevans topic = strdup("help"); 153344286Skevans break; 154344286Skevans default: 155344286Skevans command_errmsg = "usage is 'help <topic> [<subtopic>]"; 156344286Skevans close(hfd); 157344286Skevans return(CMD_ERROR); 158344286Skevans } 15940775Smsmith 160344286Skevans /* magic "index" keyword */ 161344286Skevans doindex = strcmp(topic, "index") == 0? 1 : 0; 162344286Skevans matched = doindex; 16340775Smsmith 164344286Skevans /* Scan the helpfile looking for help matching the request */ 165344286Skevans pager_open(); 166344286Skevans while (help_getnext(hfd, &t, &s, &d)) { 16740775Smsmith 168344286Skevans if (doindex) { /* dink around formatting */ 169344286Skevans if (help_emitsummary(t, s, d)) 170344286Skevans break; 17140775Smsmith 172344286Skevans } else if (strcmp(topic, t)) { 173344286Skevans /* topic mismatch */ 174344286Skevans if (matched) { 175344286Skevans /* nothing more on this topic, stop scanning */ 176344286Skevans break; 177344286Skevans } 178344286Skevans } else { 179344286Skevans /* topic matched */ 180344286Skevans matched = 1; 181344286Skevans if ((subtopic == NULL && s == NULL) || 182344286Skevans (subtopic != NULL && s != NULL && 183344286Skevans strcmp(subtopic, s) == 0)) { 184344286Skevans /* exact match, print text */ 185344286Skevans while (fgetstr(buf, 80, hfd) >= 0 && 186344286Skevans buf[0] != '#') { 187344286Skevans if (pager_output(buf)) 188344286Skevans break; 189344286Skevans if (pager_output("\n")) 190344286Skevans break; 191344286Skevans } 192344286Skevans } else if (subtopic == NULL && s != NULL) { 193344286Skevans /* topic match, list subtopics */ 194344286Skevans if (help_emitsummary(t, s, d)) 195344286Skevans break; 196344286Skevans } 19740775Smsmith } 198344286Skevans free(t); 199344286Skevans free(s); 200344286Skevans free(d); 201344286Skevans t = s = d = NULL; 20240775Smsmith } 20340775Smsmith free(t); 20440775Smsmith free(s); 20540775Smsmith free(d); 206344286Skevans pager_close(); 207344286Skevans close(hfd); 208344286Skevans if (!matched) { 209344286Skevans snprintf(command_errbuf, sizeof(command_errbuf), 210344286Skevans "no help available for '%s'", topic); 211344286Skevans free(topic); 212344286Skevans free(subtopic); 213344286Skevans return (CMD_ERROR); 214344286Skevans } 21544570Sdcs free(topic); 216329183Skevans free(subtopic); 217344286Skevans return (CMD_OK); 21838465Smsmith} 21938465Smsmith 22038465SmsmithCOMMAND_SET(commandlist, "?", "list commands", command_commandlist); 22138465Smsmith 222300146Simp/* 223300146Simp * Please note: although we use the pager for the list of commands, 224300146Simp * this routine is called from the ? FORTH function which then 225300146Simp * unconditionally prints some commands. This will lead to anomalous 226300146Simp * behavior. There's no 'pager_output' binding to FORTH to allow 227300146Simp * things to work right, so I'm documenting the bug rather than 228300156Simp * fixing it. 229300146Simp */ 23038465Smsmithstatic int 231344286Skevanscommand_commandlist(int argc __unused, char *argv[] __unused) 23238465Smsmith{ 233344286Skevans struct bootblk_command **cmdp; 234344286Skevans int res; 235344286Skevans char name[20]; 236137615Sru 237344286Skevans res = 0; 238344286Skevans pager_open(); 239344286Skevans res = pager_output("Available commands:\n"); 240344286Skevans SET_FOREACH(cmdp, Xcommand_set) { 241344286Skevans if (res) 242344286Skevans break; 243344286Skevans if ((*cmdp)->c_name != NULL && (*cmdp)->c_desc != NULL) { 244344286Skevans snprintf(name, sizeof(name), " %-15s ", 245344286Skevans (*cmdp)->c_name); 246344286Skevans pager_output(name); 247344286Skevans pager_output((*cmdp)->c_desc); 248344286Skevans res = pager_output("\n"); 249344286Skevans } 250137615Sru } 251344286Skevans pager_close(); 252344286Skevans return (CMD_OK); 25338465Smsmith} 25438465Smsmith 25538465Smsmith/* 25638465Smsmith * XXX set/show should become set/echo if we have variable 25738465Smsmith * substitution happening. 25838465Smsmith */ 25938465Smsmith 26038465SmsmithCOMMAND_SET(show, "show", "show variable(s)", command_show); 26138465Smsmith 26238465Smsmithstatic int 26338465Smsmithcommand_show(int argc, char *argv[]) 26438465Smsmith{ 265344286Skevans struct env_var *ev; 266344286Skevans char *cp; 26738465Smsmith 268344286Skevans if (argc < 2) { 269344286Skevans /* 270344286Skevans * With no arguments, print everything. 271344286Skevans */ 272344286Skevans pager_open(); 273344286Skevans for (ev = environ; ev != NULL; ev = ev->ev_next) { 274344286Skevans pager_output(ev->ev_name); 275344286Skevans cp = getenv(ev->ev_name); 276344286Skevans if (cp != NULL) { 277344286Skevans pager_output("="); 278344286Skevans pager_output(cp); 279344286Skevans } 280344286Skevans if (pager_output("\n")) 281344286Skevans break; 282344286Skevans } 283344286Skevans pager_close(); 28438465Smsmith } else { 285344286Skevans if ((cp = getenv(argv[1])) != NULL) { 286344286Skevans printf("%s\n", cp); 287344286Skevans } else { 288344286Skevans snprintf(command_errbuf, sizeof(command_errbuf), 289344286Skevans "variable '%s' not found", argv[1]); 290344286Skevans return (CMD_ERROR); 291344286Skevans } 29238465Smsmith } 293344286Skevans return (CMD_OK); 29438465Smsmith} 29538465Smsmith 29638465SmsmithCOMMAND_SET(set, "set", "set a variable", command_set); 29738465Smsmith 29838465Smsmithstatic int 29938465Smsmithcommand_set(int argc, char *argv[]) 30038465Smsmith{ 301344286Skevans int err; 302344286Skevans 303344286Skevans if (argc != 2) { 304344286Skevans command_errmsg = "wrong number of arguments"; 305344286Skevans return (CMD_ERROR); 306344286Skevans } else { 307344286Skevans if ((err = putenv(argv[1])) != 0) { 308344286Skevans command_errmsg = strerror(err); 309344286Skevans return (CMD_ERROR); 310344286Skevans } 31138465Smsmith } 312344286Skevans return (CMD_OK); 31338465Smsmith} 31438465Smsmith 31538465SmsmithCOMMAND_SET(unset, "unset", "unset a variable", command_unset); 31638465Smsmith 31738465Smsmithstatic int 318344286Skevanscommand_unset(int argc, char *argv[]) 31938465Smsmith{ 320344286Skevans int err; 321344286Skevans 322344286Skevans if (argc != 2) { 323344286Skevans command_errmsg = "wrong number of arguments"; 324344286Skevans return (CMD_ERROR); 325344286Skevans } else { 326344286Skevans if ((err = unsetenv(argv[1])) != 0) { 327344286Skevans command_errmsg = strerror(err); 328344286Skevans return (CMD_ERROR); 329344286Skevans } 33038465Smsmith } 331344286Skevans return (CMD_OK); 33238465Smsmith} 33338465Smsmith 334137667SruCOMMAND_SET(echo, "echo", "echo arguments", command_echo); 33538764Smsmith 33638764Smsmithstatic int 33738764Smsmithcommand_echo(int argc, char *argv[]) 33838764Smsmith{ 339344286Skevans char *s; 340344286Skevans int nl, ch; 341344286Skevans 342344286Skevans nl = 0; 343344286Skevans optind = 1; 344344286Skevans optreset = 1; 345344286Skevans while ((ch = getopt(argc, argv, "n")) != -1) { 346344286Skevans switch (ch) { 347344286Skevans case 'n': 348344286Skevans nl = 1; 349344286Skevans break; 350344286Skevans case '?': 351344286Skevans default: 352344286Skevans /* getopt has already reported an error */ 353344286Skevans return (CMD_OK); 354344286Skevans } 35538764Smsmith } 356344286Skevans argv += (optind); 357344286Skevans argc -= (optind); 35838764Smsmith 359344286Skevans s = unargv(argc, argv); 360344286Skevans if (s != NULL) { 361344286Skevans printf("%s", s); 362344286Skevans free(s); 363344286Skevans } 364344286Skevans if (!nl) 365344286Skevans printf("\n"); 366344286Skevans return (CMD_OK); 36738764Smsmith} 36838764Smsmith 36940015Smsmith/* 37040015Smsmith * A passable emulation of the sh(1) command of the same name. 37140015Smsmith */ 37240015Smsmith 373137667SruCOMMAND_SET(read, "read", "read input from the terminal", command_read); 37440015Smsmith 37540015Smsmithstatic int 37640015Smsmithcommand_read(int argc, char *argv[]) 37740015Smsmith{ 378344286Skevans char *prompt; 379344286Skevans int timeout; 380344286Skevans time_t when; 381344286Skevans char *cp; 382344286Skevans char *name; 383344286Skevans char buf[256]; /* XXX size? */ 384344286Skevans int c; 38540015Smsmith 386344286Skevans timeout = -1; 387344286Skevans prompt = NULL; 388344286Skevans optind = 1; 389344286Skevans optreset = 1; 390344286Skevans while ((c = getopt(argc, argv, "p:t:")) != -1) { 391344286Skevans switch (c) { 392344286Skevans case 'p': 393344286Skevans prompt = optarg; 394344286Skevans break; 395344286Skevans case 't': 396344286Skevans timeout = strtol(optarg, &cp, 0); 397344286Skevans if (cp == optarg) { 398344286Skevans snprintf(command_errbuf, 399344286Skevans sizeof(command_errbuf), 400344286Skevans "bad timeout '%s'", optarg); 401344286Skevans return (CMD_ERROR); 402344286Skevans } 403344286Skevans break; 404344286Skevans default: 405344286Skevans return (CMD_OK); 406344286Skevans } 40740015Smsmith } 40840015Smsmith 409344286Skevans argv += (optind); 410344286Skevans argc -= (optind); 411344286Skevans name = (argc > 0) ? argv[0]: NULL; 41240015Smsmith 413344286Skevans if (prompt != NULL) 414344286Skevans printf("%s", prompt); 415344286Skevans if (timeout >= 0) { 416344286Skevans when = time(NULL) + timeout; 417344286Skevans while (!ischar()) 418344286Skevans if (time(NULL) >= when) 419344286Skevans return (CMD_OK); /* is timeout an error? */ 420344286Skevans } 42140015Smsmith 422344286Skevans ngets(buf, sizeof(buf)); 423344286Skevans 424344286Skevans if (name != NULL) 425344286Skevans setenv(name, buf, 1); 426344286Skevans return (CMD_OK); 42740015Smsmith} 42840775Smsmith 42940775Smsmith/* 43043491Sjkh * File pager 43143491Sjkh */ 43243491SjkhCOMMAND_SET(more, "more", "show contents of a file", command_more); 43343491Sjkh 43443491Sjkhstatic int 43543491Sjkhcommand_more(int argc, char *argv[]) 43643491Sjkh{ 437344286Skevans int i; 438344286Skevans int res; 439344286Skevans char line[80]; 44043491Sjkh 441344286Skevans res = 0; 442344286Skevans pager_open(); 443344286Skevans for (i = 1; (i < argc) && (res == 0); i++) { 444344286Skevans snprintf(line, sizeof(line), "*** FILE %s BEGIN ***\n", 445344286Skevans argv[i]); 446344286Skevans if (pager_output(line)) 447344286Skevans break; 448344286Skevans res = page_file(argv[i]); 449344286Skevans if (!res) { 450344286Skevans snprintf(line, sizeof(line), "*** FILE %s END ***\n", 451344286Skevans argv[i]); 452344286Skevans res = pager_output(line); 453344286Skevans } 45443491Sjkh } 455344286Skevans pager_close(); 45643491Sjkh 457344286Skevans if (res == 0) 458344286Skevans return (CMD_OK); 459344286Skevans else 460344286Skevans return (CMD_ERROR); 46143491Sjkh} 46243491Sjkh 46343491Sjkhstatic int 46443491Sjkhpage_file(char *filename) 46543491Sjkh{ 466344286Skevans int result; 46743491Sjkh 468344286Skevans result = pager_file(filename); 46943491Sjkh 470344286Skevans if (result == -1) { 471344286Skevans snprintf(command_errbuf, sizeof(command_errbuf), 472344286Skevans "error showing %s", filename); 473344286Skevans } 47443491Sjkh 475344286Skevans return (result); 476344286Skevans} 47743491Sjkh 47843491Sjkh/* 47940775Smsmith * List all disk-like devices 48040775Smsmith */ 48142418SmsmithCOMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); 48240775Smsmith 48340775Smsmithstatic int 48440775Smsmithcommand_lsdev(int argc, char *argv[]) 48540775Smsmith{ 486344286Skevans int verbose, ch, i; 487344286Skevans char line[80]; 488344286Skevans 489344286Skevans verbose = 0; 490344286Skevans optind = 1; 491344286Skevans optreset = 1; 492344286Skevans while ((ch = getopt(argc, argv, "v")) != -1) { 493344286Skevans switch (ch) { 494344286Skevans case 'v': 495344286Skevans verbose = 1; 496344286Skevans break; 497344286Skevans case '?': 498344286Skevans default: 499344286Skevans /* getopt has already reported an error */ 500344286Skevans return (CMD_OK); 501344286Skevans } 50240775Smsmith } 503344286Skevans argv += (optind); 504344286Skevans argc -= (optind); 50540775Smsmith 506344286Skevans pager_open(); 507344286Skevans for (i = 0; devsw[i] != NULL; i++) { 508344286Skevans if (devsw[i]->dv_print != NULL) { 509344286Skevans if (devsw[i]->dv_print(verbose)) 510344286Skevans break; 511344286Skevans } else { 512344286Skevans snprintf(line, sizeof(line), "%s: (unknown)\n", 513344286Skevans devsw[i]->dv_name); 514344286Skevans if (pager_output(line)) 515344286Skevans break; 516344286Skevans } 51740775Smsmith } 518344286Skevans pager_close(); 519344286Skevans return (CMD_OK); 52040775Smsmith} 521