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