xref: /openbsd-src/sys/stand/boot/cmd.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: cmd.c,v 1.46 2000/01/20 19:56:48 mickey Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-1999 Michael Shalayeff
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Michael Shalayeff.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34 
35 #include <sys/param.h>
36 #include <libsa.h>
37 #include <sys/reboot.h>
38 #include "cmd.h"
39 
40 #define CTRL(c)	((c)&0x1f)
41 
42 static int Xboot __P((void));
43 static int Xecho __P((void));
44 static int Xhelp __P((void));
45 static int Xls __P((void));
46 static int Xnop __P((void));
47 static int Xreboot __P((void));
48 static int Xstty __P((void));
49 static int Xtime __P((void));
50 #ifdef MACHINE_CMD
51 static int Xmachine __P((void));
52 extern const struct cmd_table MACHINE_CMD[];
53 #endif
54 extern int Xset __P((void));
55 extern int Xenv __P((void));
56 
57 extern const struct cmd_table cmd_set[];
58 const struct cmd_table cmd_table[] = {
59 	{"#",      CMDT_CMD, Xnop},  /* XXX must be first */
60 	{"boot",   CMDT_CMD, Xboot},
61 	{"echo",   CMDT_CMD, Xecho},
62 	{"env",    CMDT_CMD, Xenv},
63 	{"help",   CMDT_CMD, Xhelp},
64 	{"ls",     CMDT_CMD, Xls},
65 #ifdef MACHINE_CMD
66 	{"machine",CMDT_MDC, Xmachine},
67 #endif
68 	{"reboot", CMDT_CMD, Xreboot},
69 	{"set",    CMDT_SET, Xset},
70 	{"stty",   CMDT_CMD, Xstty},
71 	{"time",   CMDT_CMD, Xtime},
72 	{NULL, 0},
73 };
74 
75 static void ls __P((char *, register struct stat *));
76 static int readline __P((register char *, size_t, int));
77 char *nextword __P((register char *));
78 static char *whatcmd
79 	__P((register const struct cmd_table **ct, register char *));
80 static int docmd __P((void));
81 static char *qualify __P((char *));
82 
83 char cmd_buf[133];
84 
85 int
86 getcmd()
87 {
88 	cmd.cmd = NULL;
89 
90 	if (!readline(cmd_buf, sizeof(cmd_buf), cmd.timeout))
91 		cmd.cmd = cmd_table;
92 
93 	return docmd();
94 }
95 
96 int
97 read_conf()
98 {
99 #ifndef INSECURE
100 	struct stat sb;
101 #endif
102 	int fd, eof = 0;
103 
104 	if ((fd = open(qualify(cmd.conf), 0)) < 0) {
105 		if (errno != ENOENT && errno != ENXIO) {
106 			printf("open(%s): %s\n", cmd.path, strerror(errno));
107 			return 0;
108 		}
109 		return -1;
110 	}
111 
112 #ifndef INSECURE
113 	(void) fstat(fd, &sb);
114 	if (sb.st_uid || (sb.st_mode & 2)) {
115 		printf("non-secure %s, will not proceed\n", cmd.path);
116 		close(fd);
117 		return -1;
118 	}
119 #endif
120 
121 	do {
122 		register char *p = cmd_buf;
123 
124 		cmd.cmd = NULL;
125 
126 		do
127 			eof = read(fd, p, 1);
128 		while (eof > 0 && *p++ != '\n');
129 
130 		if (eof < 0)
131 			printf("%s: %s\n", cmd.path, strerror(errno));
132 		else
133 			*--p = '\0';
134 
135 	} while (eof > 0 && !(eof = docmd()));
136 
137 	close(fd);
138 	return eof;
139 }
140 
141 static int
142 docmd()
143 {
144 	register char *p = NULL;
145 	const struct cmd_table *ct = cmd_table, *cs;
146 
147 	cmd.argc = 1;
148 	if (cmd.cmd == NULL) {
149 
150 		/* command */
151 		for (p = cmd_buf; *p && (*p == ' ' || *p == '\t'); p++)
152 			;
153 		if (*p == '#' || *p == '\0') { /* comment or empty string */
154 #ifdef DEBUG
155 			printf("rem\n");
156 #endif
157 			return 0;
158 		}
159 		ct = cmd_table;
160 		cs = NULL;
161 		cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */
162 		p = whatcmd(&ct, p);
163 		if (ct == NULL) {
164 			cmd.argc++;
165 			ct = cmd_table;
166 		} else if (ct->cmd_type == CMDT_SET && p != NULL) {
167 			cs = cmd_set;
168 #ifdef MACHINE_CMD
169 		} else if (ct->cmd_type == CMDT_MDC && p != NULL) {
170 			cs = MACHINE_CMD;
171 #endif
172 		}
173 
174 		if (cs != NULL) {
175 			p = whatcmd(&cs, p);
176 			if (cs == NULL) {
177 				printf("%s: syntax error\n", ct->cmd_name);
178 				return 0;
179 			}
180 			ct = cs;
181 		}
182 		cmd.cmd = ct;
183 	}
184 
185 	cmd.argv[0] = ct->cmd_name;
186 	while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) {
187 		cmd.argv[cmd.argc++] = p;
188 		p = nextword(p);
189 	}
190 	cmd.argv[cmd.argc] = NULL;
191 
192 	return (*cmd.cmd->cmd_exec)();
193 }
194 
195 static char *
196 whatcmd(ct, p)
197 	register const struct cmd_table **ct;
198 	register char *p;
199 {
200 	register char *q;
201 	register int l;
202 
203 	q = nextword(p);
204 
205 	for (l = 0; p[l]; l++)
206 		;
207 
208 	while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l))
209 		(*ct)++;
210 
211 	if ((*ct)->cmd_name == NULL)
212 		*ct = NULL;
213 
214 	return q;
215 }
216 
217 static int
218 readline(buf, n, to)
219 	register char *buf;
220 	size_t n;
221 	int	to;
222 {
223 #ifdef DEBUG
224 	extern int debug;
225 #endif
226 	register char *p = buf, ch;
227 
228 	/* Only do timeout if greater than 0 */
229 	if (to > 0) {
230 		u_long i = 0;
231 		time_t tt = getsecs() + to;
232 #ifdef DEBUG
233 		if (debug > 2)
234 			printf ("readline: timeout(%d) at %u\n", to, tt);
235 #endif
236 		/* check for timeout expiration less often
237 		   (for some very constrained archs) */
238 		while (!cnischar())
239 			if (!(i++ % 1000) && (getsecs() >= tt))
240 				break;
241 
242 		if (!cnischar()) {
243 			strncpy(buf, "boot", 5);
244 			putchar('\n');
245 			return strlen(buf);
246 		}
247 	} else
248 		while (!cnischar()) ;
249 
250 	while (1) {
251 		switch ((ch = getchar())) {
252 		case CTRL('u'):
253 			while (p-- > buf)
254 				putchar('\177');
255 			continue;
256 		case '\n':
257 		case '\r':
258 			p[1] = *p = '\0';
259 			break;
260 		case '\b':
261 		case '\177':
262 			if (p > buf) {
263 				putchar('\177');
264 				p--;
265 			}
266 			continue;
267 		default:
268 			if (p - buf < n-1)
269 				*p++ = ch;
270 			else {
271 				putchar('\007');
272 				putchar('\177');
273 			}
274 			continue;
275 		}
276 		break;
277 	}
278 
279 	return p - buf;
280 }
281 
282 /*
283  * Search for spaces/tabs after the current word. If found, \0 the
284  * first one.  Then pass a pointer to the first character of the
285  * next word, or NULL if there is no next word.
286  */
287 char *
288 nextword(p)
289 	register char *p;
290 {
291 	/* skip blanks */
292 	while (*p && *p != '\t' && *p != ' ')
293 		p++;
294 	if (*p) {
295 		*p++ = '\0';
296 		while (*p == '\t' || *p == ' ')
297 			p++;
298 	}
299 	if (*p == '\0')
300 		p = NULL;
301 	return p;
302 }
303 
304 static void
305 print_help(ct)
306 	register const struct cmd_table *ct;
307 {
308 	for (; ct->cmd_name != NULL; ct++)
309 		printf(" %s", ct->cmd_name);
310 	putchar('\n');
311 }
312 
313 static int
314 Xhelp()
315 {
316 	printf("commands:");
317 	print_help(cmd_table);
318 #ifdef MACHINE_CMD
319 	return Xmachine();
320 #else
321 	return 0;
322 #endif
323 }
324 
325 #ifdef MACHINE_CMD
326 static int
327 Xmachine()
328 {
329 	printf("machine:");
330 	print_help(MACHINE_CMD);
331 	return 0;
332 }
333 #endif
334 
335 static int
336 Xecho()
337 {
338 	register int i;
339 	for (i = 1; i < cmd.argc; i++)
340 		printf("%s ", cmd.argv[i]);
341 	putchar('\n');
342 	return 0;
343 }
344 
345 static int
346 Xstty()
347 {
348 	register int sp;
349 	register char *cp;
350 	dev_t dev;
351 
352 	if (cmd.argc == 1)
353 		printf("%s speed is %d\n", ttyname(0), cnspeed(0, -1));
354 	else {
355 		dev = ttydev(cmd.argv[1]);
356 		if (dev == NODEV)
357 			printf("%s not a console device\n", cmd.argv[1]);
358 		else {
359 			if (cmd.argc == 2)
360 				printf("%s speed is %d\n", cmd.argv[1],
361 				       cnspeed(dev, -1));
362 			else {
363 				sp = 0;
364 				for (cp = cmd.argv[2]; *cp && isdigit(*cp); cp++)
365 					sp = sp * 10 + (*cp - '0');
366 				cnspeed(dev, sp);
367 			}
368 		}
369 	}
370 
371 	return 0;
372 }
373 
374 static int
375 Xtime()
376 {
377 	time_t tt = getsecs();
378 
379 	if (cmd.argc == 1)
380 		printf(ctime(&tt));
381 	else {
382 	}
383 
384 	return 0;
385 }
386 
387 static int
388 Xls()
389 {
390 	struct stat sb;
391 	register char *p;
392 	int fd;
393 
394 	if (stat(qualify((cmd.argv[1]? cmd.argv[1]: "/.")), &sb) < 0) {
395 		printf("stat(%s): %s\n", cmd.path, strerror(errno));
396 		return 0;
397 	}
398 
399 	if ((sb.st_mode & S_IFMT) != S_IFDIR)
400 		ls(cmd.path, &sb);
401 	else {
402 		if ((fd = opendir(cmd.path)) < 0) {
403 			printf ("opendir(%s): %s\n", cmd.path,
404 				strerror(errno));
405 			return 0;
406 		}
407 
408 		/* no strlen in lib !!! */
409 		for (p = cmd.path; *p; p++);
410 		*p++ = '/';
411 		*p = '\0';
412 
413 		while(readdir(fd, p) >= 0) {
414 			if (stat(cmd.path, &sb) < 0)
415 				printf("stat(%s): %s\n", cmd.path,
416 				       strerror(errno));
417 			else
418 				ls(p, &sb);
419 		}
420 		closedir (fd);
421 	}
422 	return 0;
423 }
424 
425 #define lsrwx(mode,s) \
426 	putchar ((mode) & S_IROTH? 'r' : '-'); \
427 	putchar ((mode) & S_IWOTH? 'w' : '-'); \
428 	putchar ((mode) & S_IXOTH? *(s): (s)[1]);
429 
430 static void
431 ls(name, sb)
432 	register char *name;
433 	register struct stat *sb;
434 {
435 	putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]);
436 	lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-"));
437 	lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-"));
438 	lsrwx(sb->st_mode     , (sb->st_mode & S_ISTXT? "tT" : "x-"));
439 
440 	printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid,
441 		(u_long)sb->st_size, name);
442 }
443 #undef lsrwx
444 
445 int doboot = 1;
446 
447 static int
448 Xnop()
449 {
450 	if (doboot) {
451 		doboot = 0;
452 		return (Xboot());
453 	}
454 
455 	return 0;
456 }
457 
458 static int
459 Xboot()
460 {
461 	if (cmd.argc > 1 && cmd.argv[1][0] != '-') {
462 		qualify((cmd.argv[1]? cmd.argv[1]: cmd.image));
463 		if (bootparse(2))
464 			return 0;
465 	} else {
466 		if (bootparse(1))
467 			return 0;
468 		sprintf(cmd.path, "%s:%s", cmd.bootdev, cmd.image);
469 	}
470 
471 	return 1;
472 }
473 
474 /*
475  * Qualifies the path adding neccessary dev
476  */
477 
478 static char *
479 qualify(name)
480 	char *name;
481 {
482 	register char *p;
483 
484 	for (p = name; *p; p++)
485 		if (*p == ':')
486 			break;
487 	if (*p == ':')
488 		strncpy(cmd.path, name, sizeof(cmd.path));
489 	else
490 		sprintf(cmd.path, "%s:%s", cmd.bootdev, name);
491 	return cmd.path;
492 }
493 
494 static int
495 Xreboot()
496 {
497 	printf("Rebooting...\n");
498 	exit();
499 	return 0; /* just in case */
500 }
501 
502