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