xref: /openbsd-src/sys/stand/boot/cmd.c (revision a4f11372d5ec16405c3947a49e9200b89358d82d)
1 /*	$OpenBSD: cmd.c,v 1.70 2023/02/23 19:48:22 miod 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 #ifdef BOOT_STTY
47 static int Xstty(void);
48 #endif
49 static int Xtime(void);
50 #ifdef MACHINE_CMD
51 static int Xmachine(void);
52 extern const struct cmd_table MACHINE_CMD[];
53 #endif
54 extern int Xset(void);
55 extern int Xenv(void);
56 
57 #ifdef CHECK_SKIP_CONF
58 extern int CHECK_SKIP_CONF(void);
59 #endif
60 
61 extern const struct cmd_table cmd_set[];
62 const struct cmd_table cmd_table[] = {
63 	{"#",      CMDT_CMD, Xnop},  /* XXX must be first */
64 	{"boot",   CMDT_CMD, Xboot},
65 	{"echo",   CMDT_CMD, Xecho},
66 	{"env",    CMDT_CMD, Xenv},
67 	{"help",   CMDT_CMD, Xhelp},
68 	{"hexdump",CMDT_CMD, Xhexdump},
69 	{"ls",     CMDT_CMD, Xls},
70 #ifdef MACHINE_CMD
71 	{"machine",CMDT_MDC, Xmachine},
72 #endif
73 	{"reboot", CMDT_CMD, Xreboot},
74 	{"set",    CMDT_SET, Xset},
75 #ifdef BOOT_STTY
76 	{"stty",   CMDT_CMD, Xstty},
77 #endif
78 	{"time",   CMDT_CMD, Xtime},
79 	{NULL, 0},
80 };
81 
82 static void ls(char *, struct stat *);
83 static int readline(char *, size_t, int);
84 char *nextword(char *);
85 static char *whatcmd(const struct cmd_table **ct, char *);
86 static char *qualify(char *);
87 
88 char cmd_buf[CMD_BUFF_SIZE];
89 
90 int
getcmd(void)91 getcmd(void)
92 {
93 	cmd.cmd = NULL;
94 
95 	if (!readline(cmd_buf, sizeof(cmd_buf), cmd.timeout))
96 		cmd.cmd = cmd_table;
97 
98 	return docmd();
99 }
100 
101 int
read_conf(void)102 read_conf(void)
103 {
104 #ifndef INSECURE
105 	struct stat sb;
106 #endif
107 	int fd, rc = 0;
108 
109 #ifdef CHECK_SKIP_CONF
110 	if (CHECK_SKIP_CONF()) {
111 		printf("boot.conf processing skipped at operator request\n");
112 		cmd.timeout = 0;
113 		return -1;		/* Pretend file wasn't found */
114 	}
115 #endif
116 
117 	if ((fd = open(qualify(cmd.conf), O_RDONLY)) < 0) {
118 		if (errno != ENOENT && errno != ENXIO) {
119 			printf("open(%s): %s\n", cmd.path, strerror(errno));
120 			return 0;
121 		}
122 		return -1;
123 	}
124 
125 #ifndef INSECURE
126 	(void) fstat(fd, &sb);
127 	if (sb.st_uid || (sb.st_mode & 2)) {
128 		printf("non-secure %s, will not proceed\n", cmd.path);
129 		close(fd);
130 		return -1;
131 	}
132 #endif
133 
134 	do {
135 		char *p = cmd_buf;
136 
137 		cmd.cmd = NULL;
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 			*p = '\0';
164 		}
165 	} while (rc > 0 && !(rc = docmd()));
166 
167 	close(fd);
168 	return rc;
169 }
170 
171 int
docmd(void)172 docmd(void)
173 {
174 	char *p = NULL;
175 	const struct cmd_table *ct = cmd_table, *cs;
176 
177 	cmd.argc = 1;
178 	if (cmd.cmd == NULL) {
179 
180 		/* command */
181 		for (p = cmd_buf; *p == ' ' || *p == '\t'; p++)
182 			;
183 		if (*p == '#' || *p == '\0') { /* comment or empty string */
184 #ifdef DEBUG
185 			printf("rem\n");
186 #endif
187 			return 0;
188 		}
189 		ct = cmd_table;
190 		cs = NULL;
191 		cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */
192 		p = whatcmd(&ct, p);
193 		if (ct == NULL) {
194 			cmd.argc++;
195 			ct = cmd_table;
196 		} else if (ct->cmd_type == CMDT_SET && p != NULL) {
197 			cs = cmd_set;
198 #ifdef MACHINE_CMD
199 		} else if (ct->cmd_type == CMDT_MDC && p != NULL) {
200 			cs = MACHINE_CMD;
201 #endif
202 		}
203 
204 		if (cs != NULL) {
205 			p = whatcmd(&cs, p);
206 			if (cs == NULL) {
207 				printf("%s: syntax error\n", ct->cmd_name);
208 				return 0;
209 			}
210 			ct = cs;
211 		}
212 		cmd.cmd = ct;
213 	}
214 
215 	cmd.argv[0] = ct->cmd_name;
216 	while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) {
217 		cmd.argv[cmd.argc++] = p;
218 		p = nextword(p);
219 	}
220 	cmd.argv[cmd.argc] = NULL;
221 
222 	return (*cmd.cmd->cmd_exec)();
223 }
224 
225 static char *
whatcmd(const struct cmd_table ** ct,char * p)226 whatcmd(const struct cmd_table **ct, char *p)
227 {
228 	char *q;
229 	int l;
230 
231 	q = nextword(p);
232 
233 	for (l = 0; p[l]; l++)
234 		;
235 
236 	while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l))
237 		(*ct)++;
238 
239 	if ((*ct)->cmd_name == NULL)
240 		*ct = NULL;
241 
242 	return q;
243 }
244 
245 static int
readline(char * buf,size_t n,int to)246 readline(char *buf, size_t n, int to)
247 {
248 #ifdef DEBUG
249 	extern int debug;
250 #endif
251 	char *p = buf, ch;
252 
253 	/* Only do timeout if greater than 0 */
254 	if (to > 0) {
255 		time_t tt = getsecs() + to;
256 #ifdef DEBUG
257 		if (debug > 2)
258 			printf ("readline: timeout(%d) at %u\n", to, tt);
259 #endif
260 		while (!cnischar() && getsecs() < tt)
261 			continue;
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 *
nextword(char * p)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
print_help(const struct cmd_table * ct)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
Xhelp(void)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
Xhexdump(void)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
Xmachine(void)377 Xmachine(void)
378 {
379 	printf("machine:");
380 	print_help(MACHINE_CMD);
381 	return 0;
382 }
383 #endif
384 
385 static int
Xecho(void)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 #ifdef BOOT_STTY
397 static int
Xstty(void)398 Xstty(void)
399 {
400 	int sp;
401 	char *cp;
402 	dev_t dev;
403 
404 	if (cmd.argc == 1) {
405 		printf("%s speed is %d\n", ttyname(0), cnspeed(0, -1));
406 		return 0;
407 	}
408 	dev = ttydev(cmd.argv[1]);
409 	if (dev == NODEV) {
410 		printf("%s not a console device\n", cmd.argv[1]);
411 		return 0;
412 	}
413 
414 	if (cmd.argc == 2)
415 		printf("%s speed is %d\n", cmd.argv[1],
416 		    cnspeed(dev, -1));
417 	else {
418 		sp = 0;
419 		for (cp = cmd.argv[2]; isdigit(*cp); cp++)
420 			sp = sp * 10 + (*cp - '0');
421 		cnspeed(dev, sp);
422 	}
423 	return 0;
424 }
425 #endif
426 
427 static int
Xtime(void)428 Xtime(void)
429 {
430 	time_t tt = getsecs();
431 
432 	if (cmd.argc == 1)
433 		printf(ctime(&tt));
434 
435 	return 0;
436 }
437 
438 static int
Xls(void)439 Xls(void)
440 {
441 	struct stat sb;
442 	char *p;
443 	int fd;
444 
445 	if (stat(qualify((cmd.argv[1]? cmd.argv[1]: "/.")), &sb) < 0) {
446 		printf("stat(%s): %s\n", cmd.path, strerror(errno));
447 		return 0;
448 	}
449 
450 	if ((sb.st_mode & S_IFMT) != S_IFDIR)
451 		ls(cmd.path, &sb);
452 	else {
453 		if ((fd = opendir(cmd.path)) < 0) {
454 			printf("opendir(%s): %s\n", cmd.path,
455 			    strerror(errno));
456 			return 0;
457 		}
458 
459 		/* no strlen in lib !!! */
460 		for (p = cmd.path; *p; p++)
461 			;
462 		*p++ = '/';
463 		*p = '\0';
464 
465 		while (readdir(fd, p) >= 0) {
466 			if (stat(cmd.path, &sb) < 0)
467 				printf("stat(%s): %s\n", cmd.path,
468 				    strerror(errno));
469 			else
470 				ls(p, &sb);
471 		}
472 		closedir (fd);
473 	}
474 	return 0;
475 }
476 
477 #define lsrwx(mode,s) \
478 	putchar ((mode) & S_IROTH? 'r' : '-'); \
479 	putchar ((mode) & S_IWOTH? 'w' : '-'); \
480 	putchar ((mode) & S_IXOTH? *(s): (s)[1]);
481 
482 static void
ls(char * name,struct stat * sb)483 ls(char *name, struct stat *sb)
484 {
485 	putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]);
486 	lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-"));
487 	lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-"));
488 	lsrwx(sb->st_mode     , (sb->st_mode & S_ISTXT? "tT" : "x-"));
489 
490 	printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid,
491 	    (u_long)sb->st_size, name);
492 }
493 #undef lsrwx
494 
495 int doboot = 1;
496 
497 static int
Xnop(void)498 Xnop(void)
499 {
500 	if (doboot) {
501 		doboot = 0;
502 		return (Xboot());
503 	}
504 
505 	return 0;
506 }
507 
508 static int
Xboot(void)509 Xboot(void)
510 {
511 	if (cmd.argc > 1 && cmd.argv[1][0] != '-') {
512 		qualify((cmd.argv[1]? cmd.argv[1]: cmd.image));
513 		if (bootparse(2))
514 			return 0;
515 	} else {
516 		if (bootparse(1))
517 			return 0;
518 		snprintf(cmd.path, sizeof cmd.path, "%s:%s",
519 		    cmd.bootdev, cmd.image);
520 	}
521 
522 	return 1;
523 }
524 
525 /*
526  * Qualifies the path adding necessary dev
527  */
528 
529 static char *
qualify(char * name)530 qualify(char *name)
531 {
532 	char *p;
533 
534 	for (p = name; *p; p++)
535 		if (*p == ':')
536 			break;
537 	if (*p == ':')
538 		strlcpy(cmd.path, name, sizeof(cmd.path));
539 	else
540 		snprintf(cmd.path, sizeof cmd.path, "%s:%s",
541 		    cmd.bootdev, name);
542 	return cmd.path;
543 }
544 
545 static int
Xreboot(void)546 Xreboot(void)
547 {
548 	printf("Rebooting...\n");
549 	exit();
550 	return 0; /* just in case */
551 }
552 
553 int
upgrade(void)554 upgrade(void)
555 {
556 	struct stat sb;
557 
558 	if (stat(qualify(("/bsd.upgrade")), &sb) < 0)
559 		return 0;
560 	if ((sb.st_mode & S_IXUSR) == 0) {
561 		printf("/bsd.upgrade is not u+x\n");
562 		return 0;
563 	}
564 	return 1;
565 }
566