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