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