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