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: src/sys/boot/common/interp.c,v 1.29 2003/08/25 23:30:41 obrien Exp $ 27 */ 28 29 /* 30 * Simple commandline interpreter, toplevel and misc. 31 */ 32 33 #include <stand.h> 34 #include <string.h> 35 #include "bootstrap.h" 36 #include "dloader.h" 37 38 static void prompt(void); 39 static int iseol(char c); 40 static void skipeol(int fd); 41 42 /* 43 * Perform the command 44 */ 45 int 46 perform(int argc, char *argv[]) 47 { 48 int result; 49 struct bootblk_command **cmdp; 50 bootblk_cmd_t *cmd; 51 const char *av0; 52 53 if (argc < 1) 54 return(CMD_OK); 55 56 av0 = argv[0]; 57 if (strchr(av0, '=') != NULL) 58 av0 = "local"; 59 60 /* set return defaults; a successful command will override these */ 61 command_errmsg = command_errbuf; 62 strcpy(command_errbuf, "no error message"); 63 cmd = NULL; 64 result = CMD_ERROR; 65 66 /* search the command set for the command */ 67 SET_FOREACH(cmdp, Xcommand_set) { 68 if (((*cmdp)->c_name != NULL) && !strcmp(av0, (*cmdp)->c_name)) { 69 cmd = (*cmdp)->c_fn; 70 break; 71 } 72 } 73 if (cmd != NULL) { 74 if ((*cmdp)->c_cond || CurrentCondition != 0) 75 result = (cmd)(argc, argv); 76 else 77 result = CMD_OK; 78 } else { 79 command_errmsg = "unknown command"; 80 } 81 return(result); 82 } 83 84 /* 85 * Interactive mode 86 */ 87 void 88 interact(void) 89 { 90 char input[256]; /* big enough? */ 91 int argc; 92 char **argv; 93 94 /* 95 * We may be booting from the boot partition, or we may be booting 96 * from the root partition with a /boot sub-directory. If the latter 97 * chdir into /boot. Ignore any error. Only rel_open() uses the chdir 98 * info. 99 */ 100 chdir("/boot"); 101 setenv("base", DirBase, 1); 102 103 /* 104 * Read our default configuration 105 */ 106 if (include("dloader.rc") != CMD_OK) 107 include("boot.conf"); 108 printf("\n"); 109 /* 110 * Before interacting, we might want to autoboot. 111 */ 112 autoboot_maybe(); 113 114 dloader_init_cmds(); 115 116 /* 117 * Not autobooting, go manual 118 */ 119 printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); 120 if (getenv("prompt") == NULL) 121 setenv("prompt", "OK", 1); 122 123 for (;;) { 124 input[0] = '\0'; 125 prompt(); 126 ngets(input, sizeof(input)); 127 if (!parse(&argc, &argv, input)) { 128 if (perform(argc, argv)) 129 printf("%s: %s\n", argv[0], command_errmsg); 130 free(argv); 131 } else { 132 printf("parse error\n"); 133 } 134 } 135 } 136 137 /* 138 * Read commands from a file, then execute them. 139 * 140 * We store the commands in memory and close the source file so that the media 141 * holding it can safely go away while we are executing. 142 * 143 * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so 144 * that the script won't stop if they fail). 145 */ 146 COMMAND_SET(include, "include", "run commands from file", command_include); 147 148 static int 149 command_include(int argc, char *argv[]) 150 { 151 int i; 152 int res; 153 char **argvbuf; 154 155 /* 156 * Since argv is static, we need to save it here. 157 */ 158 argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 159 for (i = 0; i < argc; i++) 160 argvbuf[i] = strdup(argv[i]); 161 162 res=CMD_OK; 163 for (i = 1; (i < argc) && (res == CMD_OK); i++) 164 res = include(argvbuf[i]); 165 166 for (i = 0; i < argc; i++) 167 free(argvbuf[i]); 168 free(argvbuf); 169 170 return(res); 171 } 172 173 COMMAND_SET(optinclude, "optinclude", 174 "run commands from file; ignore exit status", 175 command_optinclude); 176 177 static int 178 command_optinclude(int argc, char *argv[]) 179 { 180 int i; 181 char **argvbuf; 182 183 /* 184 * Since argv is static, we need to save it here. 185 */ 186 argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); 187 for (i = 0; i < argc; i++) 188 argvbuf[i] = strdup(argv[i]); 189 190 for (i = 1; (i < argc); i++) 191 include(argvbuf[i]); 192 193 for (i = 0; i < argc; i++) 194 free(argvbuf[i]); 195 free(argvbuf); 196 197 return(CMD_OK); 198 } 199 200 struct includeline 201 { 202 char *text; 203 int flags; 204 int line; 205 #define SL_QUIET (1<<0) 206 #define SL_IGNOREERR (1<<1) 207 struct includeline *next; 208 }; 209 210 int 211 include(const char *filename) 212 { 213 struct includeline *script, *se, *sp; 214 char input[1024]; /* big enough? */ 215 int argc,res; 216 char **argv, *cp; 217 int fd, flags, line; 218 219 if (((fd = rel_open(filename, NULL, O_RDONLY)) == -1)) { 220 command_errmsg = command_errbuf; 221 snprintf(command_errbuf, sizeof(command_errbuf), 222 "cannot find \"%s\"", filename); 223 return(CMD_ERROR); 224 } 225 226 /* 227 * Read the script into memory. 228 */ 229 script = se = NULL; 230 line = 0; 231 232 while (fgets(input, sizeof(input), fd) != NULL) { 233 line++; 234 flags = 0; 235 if(strlen(input) == sizeof(input) - 1 && 236 !iseol(input[sizeof(input) - 2])) { 237 printf("WARNING: %s: %s: Line too long: truncating; have:\n", 238 __func__, filename); 239 printf("%s\n", input); 240 skipeol(fd); 241 } 242 /* Discard comments */ 243 if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0) 244 continue; 245 cp = input; 246 /* Echo? */ 247 if (input[0] == '@') { 248 cp++; 249 flags |= SL_QUIET; 250 } 251 /* Error OK? */ 252 if (input[0] == '-') { 253 cp++; 254 flags |= SL_IGNOREERR; 255 } 256 /* Allocate script line structure and copy line, flags */ 257 sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); 258 sp->text = (char *)sp + sizeof(struct includeline); 259 strcpy(sp->text, cp); 260 sp->flags = flags; 261 sp->line = line; 262 sp->next = NULL; 263 264 if (script == NULL) { 265 script = sp; 266 } else { 267 se->next = sp; 268 } 269 se = sp; 270 } 271 close(fd); 272 273 /* 274 * Execute the script 275 */ 276 argv = NULL; 277 res = CMD_OK; 278 for (sp = script; sp != NULL; sp = sp->next) { 279 280 #if 0 281 /* print if not being quiet */ 282 if (!(sp->flags & SL_QUIET)) { 283 prompt(); 284 printf("%s\n", sp->text); 285 } 286 #endif 287 288 /* Parse the command */ 289 if (!parse(&argc, &argv, sp->text)) { 290 if ((argc > 0) && (perform(argc, argv) != 0)) { 291 /* normal command */ 292 printf("%s: %s\n", argv[0], command_errmsg); 293 if (!(sp->flags & SL_IGNOREERR)) { 294 res=CMD_ERROR; 295 break; 296 } 297 } 298 free(argv); 299 argv = NULL; 300 } else { 301 printf("%s line %d: parse error\n", filename, sp->line); 302 res=CMD_ERROR; 303 break; 304 } 305 } 306 if (argv != NULL) 307 free(argv); 308 while(script != NULL) { 309 se = script; 310 script = script->next; 311 free(se); 312 } 313 return(res); 314 } 315 316 /* 317 * Emit the current prompt; use the same syntax as the parser 318 * for embedding environment variables. 319 */ 320 static void 321 prompt(void) 322 { 323 char *pr, *p, *cp, *ev; 324 325 if ((cp = getenv("prompt")) == NULL) 326 cp = ">"; 327 pr = p = strdup(cp); 328 329 while (*p != 0) { 330 if ((*p == '$') && (*(p+1) == '{')) { 331 for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) 332 ; 333 *cp = 0; 334 ev = getenv(p + 2); 335 336 if (ev != NULL) 337 printf("%s", ev); 338 p = cp + 1; 339 continue; 340 } 341 putchar(*p++); 342 } 343 putchar(' '); 344 free(pr); 345 } 346 347 static int 348 iseol(char c) 349 { 350 return(c == '\n' || c == '\r'); 351 } 352 353 static void 354 skipeol(int fd) 355 { 356 char c; 357 358 while (read(fd, &c, 1) == 1) { 359 if (iseol(c)) 360 break; 361 } 362 } 363