xref: /titanic_52/usr/src/boot/sys/boot/common/interp.c (revision 0706f70ad68505165f24c60b8cb7d3760cd72f11)
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 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 /*
31  * Simple commandline interpreter, toplevel and misc.
32  *
33  * XXX may be obsoleted by BootFORTH or some other, better, interpreter.
34  */
35 
36 #include <stand.h>
37 #include <string.h>
38 #include "bootstrap.h"
39 
40 #ifdef BOOT_FORTH
41 #include "ficl.h"
42 #define	RETURN(x)	ficlStackPushInteger(ficlVmGetDataStack(bf_vm),!x); return(x)
43 
44 extern ficlVm *bf_vm;
45 #else
46 #define	RETURN(x)	return(x)
47 #endif
48 
49 #include "linenoise/linenoise.h"
50 
51 #define	MAXARGS	20		/* maximum number of arguments allowed */
52 
53 static char *prompt(void);
54 
55 #ifndef BOOT_FORTH
56 static int	perform(int argc, char *argv[]);
57 
58 /*
59  * Perform the command
60  */
61 int
62 perform(int argc, char *argv[])
63 {
64     int				result;
65     struct bootblk_command	**cmdp;
66     bootblk_cmd_t		*cmd;
67 
68     if (argc < 1)
69 	return(CMD_OK);
70 
71     /* set return defaults; a successful command will override these */
72     command_errmsg = command_errbuf;
73     strcpy(command_errbuf, "no error message");
74     cmd = NULL;
75     result = CMD_ERROR;
76 
77     /* search the command set for the command */
78     SET_FOREACH(cmdp, Xcommand_set) {
79 	if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name))
80 	    cmd = (*cmdp)->c_fn;
81     }
82     if (cmd != NULL) {
83 	result = (cmd)(argc, argv);
84     } else {
85 	command_errmsg = "unknown command";
86     }
87     RETURN(result);
88 }
89 #endif	/* ! BOOT_FORTH */
90 
91 /*
92  * Interactive mode
93  */
94 void
95 interact(const char *rc)
96 {
97     char *input = NULL;
98 
99     bf_init((rc) ? "" : NULL);
100 
101     if (rc == NULL) {
102 	/* Read our default configuration. */
103 	include("/boot/loader.rc");
104     } else if (*rc != '\0')
105 	include(rc);
106 
107     printf("\n");
108 
109     /*
110      * Before interacting, we might want to autoboot.
111      */
112     autoboot_maybe();
113 
114     /*
115      * Not autobooting, go manual
116      */
117     printf("\nType '?' for a list of commands, 'help' for more detailed help.\n");
118     if (getenv("prompt") == NULL)
119 	setenv("prompt", "${interpret}", 1);
120     if (getenv("interpret") == NULL)
121         setenv("interpret", "ok", 1);
122 
123 
124     while ((input = linenoise(prompt())) != NULL) {
125 	bf_vm->sourceId.i = 0;
126 	bf_run(input);
127 	linenoiseHistoryAdd(input);
128 	free(input);
129     }
130 }
131 
132 /*
133  * Read commands from a file, then execute them.
134  *
135  * We store the commands in memory and close the source file so that the media
136  * holding it can safely go away while we are executing.
137  *
138  * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so
139  * that the script won't stop if they fail).
140  */
141 COMMAND_SET(include, "include", "read commands from a file", command_include);
142 
143 static int
144 command_include(int argc, char *argv[])
145 {
146     int		i;
147     int		res;
148     char	**argvbuf;
149 
150     /*
151      * Since argv is static, we need to save it here.
152      */
153     argvbuf = (char**) calloc((u_int)argc, sizeof(char*));
154     for (i = 0; i < argc; i++)
155 	argvbuf[i] = strdup(argv[i]);
156 
157     res=CMD_OK;
158     for (i = 1; (i < argc) && (res == CMD_OK); i++)
159 	res = include(argvbuf[i]);
160 
161     for (i = 0; i < argc; i++)
162 	free(argvbuf[i]);
163     free(argvbuf);
164 
165     return(res);
166 }
167 
168 /*
169  * Header prepended to each line. The text immediately follows the header.
170  * We try to make this short in order to save memory -- the loader has
171  * limited memory available, and some of the forth files are very long.
172  */
173 struct includeline
174 {
175     struct includeline  *next;
176     int                 line;
177     char                text[0];
178 };
179 
180 /*
181  * The PXE TFTP service allows opening exactly one connection at the time,
182  * so we need to read included file into memory, then process line by line
183  * as it may contain embedded include commands.
184  */
185 int
186 include(const char *filename)
187 {
188     struct includeline  *script, *se, *sp;
189     int res = CMD_OK;
190     int	prevsrcid, fd, line;
191     char *cp, input[256];		/* big enough? */
192 
193     if (((fd = open(filename, O_RDONLY)) == -1)) {
194 	snprintf(command_errbuf, sizeof (command_errbuf), "can't open '%s': %s",
195 	    filename, strerror(errno));
196 	return(CMD_ERROR);
197     }
198     /*
199      * Read the script into memory.
200      */
201     script = se = NULL;
202     line = 0;
203 
204     while (fgetstr(input, sizeof(input), fd) >= 0) {
205 	line++;
206 	cp = input;
207 	/* Allocate script line structure and copy line, flags */
208 	if (*cp == '\0')
209 		continue;       /* ignore empty line, save memory */
210 	if (cp[0] == '\\' && cp[1] == ' ')
211 		continue;	/* ignore comment */
212 
213 	sp = malloc(sizeof(struct includeline) + strlen(cp) + 1);
214 	/* On malloc failure (it happens!), free as much as possible and exit */
215 	if (sp == NULL) {
216 		while (script != NULL) {
217 			se = script;
218 			script = script->next;
219 			free(se);
220 		}
221 		snprintf(command_errbuf, sizeof (command_errbuf),
222 		    "file '%s' line %d: memory allocation failure - aborting",
223 		    filename, line);
224 		close(fd);
225 		return (CMD_ERROR);
226 	}
227 	strcpy(sp->text, cp);
228 	sp->line = line;
229 	sp->next = NULL;
230 
231 	if (script == NULL) {
232 		script = sp;
233 	} else {
234 		se->next = sp;
235 	}
236 	se = sp;
237     }
238     close(fd);
239 
240     /*
241      * Execute the script
242      */
243 
244     prevsrcid = bf_vm->sourceId.i;
245     bf_vm->sourceId.i = fd+1;	/* 0 is user input device */
246 
247     res = CMD_OK;
248 
249     for (sp = script; sp != NULL; sp = sp->next) {
250 	res = bf_run(sp->text);
251 	if (res != FICL_VM_STATUS_OUT_OF_TEXT) {
252 		snprintf(command_errbuf, sizeof (command_errbuf),
253 		    "Error while including %s, in the line %d:\n%s",
254 		    filename, sp->line, sp->text);
255 		res = CMD_ERROR;
256 		break;
257 	} else
258 		res = CMD_OK;
259     }
260 
261     bf_vm->sourceId.i = -1;
262     (void) bf_run("");
263     bf_vm->sourceId.i = prevsrcid;
264 
265     while(script != NULL) {
266 	se = script;
267 	script = script->next;
268 	free(se);
269     }
270 
271     return(res);
272 }
273 
274 /*
275  * Emit the current prompt; use the same syntax as the parser
276  * for embedding environment variables.
277  */
278 static char *
279 prompt(void)
280 {
281     static char promptbuf[20];	/* probably too large, but well... */
282     char	*pr, *p, *cp, *ev;
283     int n = 0;
284 
285     if ((cp = getenv("prompt")) == NULL)
286 	cp = (char *)(uintptr_t)">";
287     pr = p = strdup(cp);
288 
289     while (*p != 0) {
290 	if ((*p == '$') && (*(p+1) == '{')) {
291 	    for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++)
292 		;
293 	    *cp = 0;
294 	    ev = getenv(p + 2);
295 
296 	    if (ev != NULL)
297 		n = sprintf(promptbuf+n, "%s", ev);
298 	    p = cp + 1;
299 	    continue;
300 	}
301 	promptbuf[n++] = *p;
302 	p++;
303     }
304     if (promptbuf[n - 1] != ' ')
305 	promptbuf[n++] = ' ';
306     promptbuf[n] = '\0';
307     free(pr);
308     return (promptbuf);
309 }
310