xref: /openbsd-src/sys/stand/boot/cmd.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /*	$OpenBSD: cmd.c,v 1.69 2022/06/27 20:22:26 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 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), O_RDONLY)) < 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 		time_t tt = getsecs() + to;
252 #ifdef DEBUG
253 		if (debug > 2)
254 			printf ("readline: timeout(%d) at %u\n", to, tt);
255 #endif
256 		while (!cnischar() && getsecs() < tt)
257 			continue;
258 
259 		if (!cnischar()) {
260 			strlcpy(buf, "boot", 5);
261 			putchar('\n');
262 			return strlen(buf);
263 		}
264 	} else
265 		while (!cnischar())
266 			;
267 
268 	/* User has typed something.  Turn off timeouts. */
269 	cmd.timeout = 0;
270 
271 	while (1) {
272 		switch ((ch = getchar())) {
273 		case CTRL('u'):
274 			while (p > buf) {
275 				putchar('\177');
276 				p--;
277 			}
278 			continue;
279 		case '\n':
280 		case '\r':
281 			*p = '\0';
282 			break;
283 		case '\b':
284 		case '\177':
285 			if (p > buf) {
286 				putchar('\177');
287 				p--;
288 			}
289 			continue;
290 		default:
291 			if (ch >= ' ' && ch < '\177') {
292 				if (p - buf < n-1)
293 					*p++ = ch;
294 				else {
295 					putchar('\007');
296 					putchar('\177');
297 				}
298 			}
299 			continue;
300 		}
301 		break;
302 	}
303 
304 	return p - buf;
305 }
306 
307 /*
308  * Search for spaces/tabs after the current word. If found, \0 the
309  * first one.  Then pass a pointer to the first character of the
310  * next word, or NULL if there is no next word.
311  */
312 char *
313 nextword(char *p)
314 {
315 	/* skip blanks */
316 	while (*p && *p != '\t' && *p != ' ')
317 		p++;
318 	if (*p) {
319 		*p++ = '\0';
320 		while (*p == '\t' || *p == ' ')
321 			p++;
322 	}
323 	if (*p == '\0')
324 		p = NULL;
325 	return p;
326 }
327 
328 static void
329 print_help(const struct cmd_table *ct)
330 {
331 	for (; ct->cmd_name != NULL; ct++)
332 		printf(" %s", ct->cmd_name);
333 	putchar('\n');
334 }
335 
336 static int
337 Xhelp(void)
338 {
339 	printf("commands:");
340 	print_help(cmd_table);
341 #ifdef MACHINE_CMD
342 	return Xmachine();
343 #else
344 	return 0;
345 #endif
346 }
347 
348 static int
349 Xhexdump(void)
350 {
351 	long long val[2];
352 	char *ep;
353 	int i;
354 
355 	if (cmd.argc != 3) {
356 		printf("hexdump addr size\n");
357 		return 0;
358 	}
359 
360 	for (i = 1; i < cmd.argc; i++) {
361 		val[i-1] = strtoll(cmd.argv[i], &ep, 0);
362 		if (cmd.argv[i][0] == '\0' || *ep != '\0') {
363 			printf("bad '%c' in \"%s\"\n", *ep, cmd.argv[i]);
364 			return 0;
365 		}
366 	}
367 	hexdump((void *)(unsigned long)val[0], val[1]);
368 	return 0;
369 }
370 
371 #ifdef MACHINE_CMD
372 static int
373 Xmachine(void)
374 {
375 	printf("machine:");
376 	print_help(MACHINE_CMD);
377 	return 0;
378 }
379 #endif
380 
381 static int
382 Xecho(void)
383 {
384 	int i;
385 
386 	for (i = 1; i < cmd.argc; i++)
387 		printf("%s ", cmd.argv[i]);
388 	putchar('\n');
389 	return 0;
390 }
391 
392 static int
393 Xstty(void)
394 {
395 	int sp;
396 	char *cp;
397 	dev_t dev;
398 
399 	if (cmd.argc == 1) {
400 		printf("%s speed is %d\n", ttyname(0), cnspeed(0, -1));
401 		return 0;
402 	}
403 	dev = ttydev(cmd.argv[1]);
404 	if (dev == NODEV) {
405 		printf("%s not a console device\n", cmd.argv[1]);
406 		return 0;
407 	}
408 
409 	if (cmd.argc == 2)
410 		printf("%s speed is %d\n", cmd.argv[1],
411 		    cnspeed(dev, -1));
412 	else {
413 		sp = 0;
414 		for (cp = cmd.argv[2]; isdigit(*cp); cp++)
415 			sp = sp * 10 + (*cp - '0');
416 		cnspeed(dev, sp);
417 	}
418 	return 0;
419 }
420 
421 static int
422 Xtime(void)
423 {
424 	time_t tt = getsecs();
425 
426 	if (cmd.argc == 1)
427 		printf(ctime(&tt));
428 
429 	return 0;
430 }
431 
432 static int
433 Xls(void)
434 {
435 	struct stat sb;
436 	char *p;
437 	int fd;
438 
439 	if (stat(qualify((cmd.argv[1]? cmd.argv[1]: "/.")), &sb) < 0) {
440 		printf("stat(%s): %s\n", cmd.path, strerror(errno));
441 		return 0;
442 	}
443 
444 	if ((sb.st_mode & S_IFMT) != S_IFDIR)
445 		ls(cmd.path, &sb);
446 	else {
447 		if ((fd = opendir(cmd.path)) < 0) {
448 			printf("opendir(%s): %s\n", cmd.path,
449 			    strerror(errno));
450 			return 0;
451 		}
452 
453 		/* no strlen in lib !!! */
454 		for (p = cmd.path; *p; p++)
455 			;
456 		*p++ = '/';
457 		*p = '\0';
458 
459 		while (readdir(fd, p) >= 0) {
460 			if (stat(cmd.path, &sb) < 0)
461 				printf("stat(%s): %s\n", cmd.path,
462 				    strerror(errno));
463 			else
464 				ls(p, &sb);
465 		}
466 		closedir (fd);
467 	}
468 	return 0;
469 }
470 
471 #define lsrwx(mode,s) \
472 	putchar ((mode) & S_IROTH? 'r' : '-'); \
473 	putchar ((mode) & S_IWOTH? 'w' : '-'); \
474 	putchar ((mode) & S_IXOTH? *(s): (s)[1]);
475 
476 static void
477 ls(char *name, struct stat *sb)
478 {
479 	putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]);
480 	lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-"));
481 	lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-"));
482 	lsrwx(sb->st_mode     , (sb->st_mode & S_ISTXT? "tT" : "x-"));
483 
484 	printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid,
485 	    (u_long)sb->st_size, name);
486 }
487 #undef lsrwx
488 
489 int doboot = 1;
490 
491 static int
492 Xnop(void)
493 {
494 	if (doboot) {
495 		doboot = 0;
496 		return (Xboot());
497 	}
498 
499 	return 0;
500 }
501 
502 static int
503 Xboot(void)
504 {
505 	if (cmd.argc > 1 && cmd.argv[1][0] != '-') {
506 		qualify((cmd.argv[1]? cmd.argv[1]: cmd.image));
507 		if (bootparse(2))
508 			return 0;
509 	} else {
510 		if (bootparse(1))
511 			return 0;
512 		snprintf(cmd.path, sizeof cmd.path, "%s:%s",
513 		    cmd.bootdev, cmd.image);
514 	}
515 
516 	return 1;
517 }
518 
519 /*
520  * Qualifies the path adding necessary dev
521  */
522 
523 static char *
524 qualify(char *name)
525 {
526 	char *p;
527 
528 	for (p = name; *p; p++)
529 		if (*p == ':')
530 			break;
531 	if (*p == ':')
532 		strlcpy(cmd.path, name, sizeof(cmd.path));
533 	else
534 		snprintf(cmd.path, sizeof cmd.path, "%s:%s",
535 		    cmd.bootdev, name);
536 	return cmd.path;
537 }
538 
539 static int
540 Xreboot(void)
541 {
542 	printf("Rebooting...\n");
543 	exit();
544 	return 0; /* just in case */
545 }
546 
547 int
548 upgrade(void)
549 {
550 	struct stat sb;
551 
552 	if (stat(qualify(("/bsd.upgrade")), &sb) < 0)
553 		return 0;
554 	if ((sb.st_mode & S_IXUSR) == 0) {
555 		printf("/bsd.upgrade is not u+x\n");
556 		return 0;
557 	}
558 	return 1;
559 }
560