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