xref: /openbsd/sys/arch/octeon/stand/rdboot/cmd.c (revision 3cab2bb3)
1 /*	$OpenBSD: cmd.c,v 1.3 2019/08/01 04:52:56 visa 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 #include <sys/select.h>
32 #include <sys/stat.h>
33 
34 #include <dirent.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <termios.h>
40 #include <unistd.h>
41 
42 #include "cmd.h"
43 #include "disk.h"
44 
45 static int Xboot(void);
46 static int Xecho(void);
47 static int Xhelp(void);
48 static int Xls(void);
49 static int Xnop(void);
50 static int Xreboot(void);
51 #ifdef MACHINE_CMD
52 static int Xmachine(void);
53 extern const struct cmd_table MACHINE_CMD[];
54 #endif
55 extern int Xset(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 	{"help",   CMDT_CMD, Xhelp},
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 	{NULL, 0},
74 };
75 
76 static void ls(const 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 	struct stat sb;
99 	const char *path;
100 	int fd, rc = 0;
101 
102 #ifdef CHECK_SKIP_CONF
103 	if (CHECK_SKIP_CONF()) {
104 		printf("boot.conf processing skipped at operator request\n");
105 		cmd.timeout = 0;
106 		return -1;		/* Pretend file wasn't found */
107 	}
108 #endif
109 
110 	path = disk_open(qualify(cmd.conf));
111 	if (path == NULL) {
112 		fprintf(stderr, "cannot open device for reading %s: %s\n",
113 		    cmd.conf, strerror(errno));
114 		return -1;
115 	}
116 	if ((fd = open(path, O_RDONLY)) == -1) {
117 		if (errno != ENOENT && errno != ENXIO) {
118 			fprintf(stderr, "%s: open(%s): %s\n", __func__,
119 			    cmd.path, strerror(errno));
120 			rc = 0;
121 		} else
122 			rc = -1;
123 		goto out;
124 	}
125 
126 	(void) fstat(fd, &sb);
127 	if (sb.st_uid || (sb.st_mode & 2)) {
128 		fprintf(stderr, "non-secure %s, will not proceed\n", cmd.path);
129 		rc = -1;
130 		goto out;
131 	}
132 
133 	do {
134 		char *p = cmd_buf;
135 
136 		cmd.cmd = NULL;
137 		do {
138 			rc = read(fd, p, 1);
139 		} while (rc > 0 && *p++ != '\n' &&
140 		    (p-cmd_buf) < sizeof(cmd_buf));
141 
142 		if (rc < 0) {			/* Error from read() */
143 			fprintf(stderr, "%s: %s\n", cmd.path, strerror(errno));
144 			break;
145 		}
146 
147 		if (rc == 0) {			/* eof from read() */
148 			if (p != cmd_buf) {	/* Line w/o trailing \n */
149 				*p = '\0';
150 				rc = docmd();
151 				break;
152 			}
153 		} else {			/* rc > 0, read a char */
154 			p--;			/* Get back to last character */
155 
156 			if (*p != '\n') {	/* Line was too long */
157 				fprintf(stderr, "%s: line too long\n",
158 				    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 out:
168 	if (fd != -1)
169 		close(fd);
170 	disk_close();
171 	return rc;
172 }
173 
174 int
175 docmd(void)
176 {
177 	char *p = NULL;
178 	const struct cmd_table *ct = cmd_table, *cs;
179 
180 	cmd.argc = 1;
181 	if (cmd.cmd == NULL) {
182 
183 		/* command */
184 		for (p = cmd_buf; *p == ' ' || *p == '\t'; p++)
185 			;
186 		if (*p == '#' || *p == '\0') { /* comment or empty string */
187 #ifdef DEBUG
188 			printf("rem\n");
189 #endif
190 			return 0;
191 		}
192 		ct = cmd_table;
193 		cs = NULL;
194 		cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */
195 		p = whatcmd(&ct, p);
196 		if (ct == NULL) {
197 			cmd.argc++;
198 			ct = cmd_table;
199 		} else if (ct->cmd_type == CMDT_SET && p != NULL) {
200 			cs = cmd_set;
201 #ifdef MACHINE_CMD
202 		} else if (ct->cmd_type == CMDT_MDC && p != NULL) {
203 			cs = MACHINE_CMD;
204 #endif
205 		}
206 
207 		if (cs != NULL) {
208 			p = whatcmd(&cs, p);
209 			if (cs == NULL) {
210 				printf("%s: syntax error\n", ct->cmd_name);
211 				return 0;
212 			}
213 			ct = cs;
214 		}
215 		cmd.cmd = ct;
216 	}
217 
218 	cmd.argv[0] = ct->cmd_name;
219 	while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) {
220 		cmd.argv[cmd.argc++] = p;
221 		p = nextword(p);
222 	}
223 	cmd.argv[cmd.argc] = NULL;
224 
225 	return (*cmd.cmd->cmd_exec)();
226 }
227 
228 static char *
229 whatcmd(const struct cmd_table **ct, char *p)
230 {
231 	char *q;
232 	int l;
233 
234 	q = nextword(p);
235 
236 	for (l = 0; p[l]; l++)
237 		;
238 
239 	while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l))
240 		(*ct)++;
241 
242 	if ((*ct)->cmd_name == NULL)
243 		*ct = NULL;
244 
245 	return q;
246 }
247 
248 static int
249 readline(char *buf, size_t n, int to)
250 {
251 	struct termios saved_tio, tio;
252 	struct timeval tv;
253 	fd_set fdset;
254 	char *p;
255 	int timed_out = 0;
256 #ifdef DEBUG
257 	extern int debug;
258 #endif
259 
260 	/* Only do timeout if greater than 0 */
261 	if (to > 0) {
262 		/* Switch to non-canonical mode for timeout detection. */
263 		tcgetattr(STDIN_FILENO, &saved_tio);
264 		tio = saved_tio;
265 		tio.c_lflag &= ~(ECHO | ICANON);
266 		tcsetattr(STDIN_FILENO, TCSANOW, &tio);
267 
268 		FD_ZERO(&fdset);
269 		FD_SET(STDIN_FILENO, &fdset);
270 		tv.tv_sec = to;
271 		tv.tv_usec = 0;
272 		if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &tv) == 0)
273 			timed_out = 1;
274 
275 		/* Restore canonical mode. */
276 		tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio);
277 
278 		if (timed_out) {
279 			strlcpy(buf, "boot", 5);
280 			putchar('\n');
281 			return strlen(buf);
282 		}
283 	}
284 
285 	/* User has typed something.  Turn off timeouts. */
286 	cmd.timeout = 0;
287 
288 	if (fgets(buf, n, stdin) == NULL)
289 		return 0;
290 
291 	/* Strip trailing newline. */
292 	p = strchr(buf, '\n');
293 	if (p != NULL)
294 		*p = '\0';
295 
296 	return strlen(buf);
297 }
298 
299 /*
300  * Search for spaces/tabs after the current word. If found, \0 the
301  * first one.  Then pass a pointer to the first character of the
302  * next word, or NULL if there is no next word.
303  */
304 char *
305 nextword(char *p)
306 {
307 	/* skip blanks */
308 	while (*p && *p != '\t' && *p != ' ')
309 		p++;
310 	if (*p) {
311 		*p++ = '\0';
312 		while (*p == '\t' || *p == ' ')
313 			p++;
314 	}
315 	if (*p == '\0')
316 		p = NULL;
317 	return p;
318 }
319 
320 static void
321 print_help(const struct cmd_table *ct)
322 {
323 	for (; ct->cmd_name != NULL; ct++)
324 		printf(" %s", ct->cmd_name);
325 	putchar('\n');
326 }
327 
328 static int
329 Xhelp(void)
330 {
331 	printf("commands:");
332 	print_help(cmd_table);
333 #ifdef MACHINE_CMD
334 	return Xmachine();
335 #else
336 	return 0;
337 #endif
338 }
339 
340 #ifdef MACHINE_CMD
341 static int
342 Xmachine(void)
343 {
344 	printf("machine:");
345 	print_help(MACHINE_CMD);
346 	return 0;
347 }
348 #endif
349 
350 static int
351 Xecho(void)
352 {
353 	int i;
354 
355 	for (i = 1; i < cmd.argc; i++)
356 		printf("%s ", cmd.argv[i]);
357 	putchar('\n');
358 	return 0;
359 }
360 
361 static int
362 Xls(void)
363 {
364 	struct stat sb;
365 	const char *path;
366 	DIR *dir;
367 	struct dirent *dent;
368 	int dirfd, oldcwd;
369 
370 	path = disk_open(qualify(cmd.argv[1] ? cmd.argv[1] : "/."));
371 	if (path == NULL)
372 		return 0;
373 
374 	if (stat(path, &sb) < 0) {
375 		printf("stat(%s): %s\n", cmd.path, strerror(errno));
376 		goto out;
377 	}
378 
379 	if ((sb.st_mode & S_IFMT) != S_IFDIR)
380 		ls(path, &sb);
381 	else {
382 		oldcwd = open(".", O_RDONLY);
383 
384 		dirfd = open(path, O_RDONLY);
385 		if (dirfd < 0) {
386 			printf("opendir(%s): %s\n", cmd.path, strerror(errno));
387 			close(oldcwd);
388 			goto out;
389 		}
390 		if ((dir = fdopendir(dirfd)) < 0) {
391 			printf("opendir(%s): %s\n", cmd.path, strerror(errno));
392 			close(dirfd);
393 			close(oldcwd);
394 			goto out;
395 		}
396 		fchdir(dirfd);
397 		while ((dent = readdir(dir)) != NULL) {
398 			if (fstatat(dirfd, dent->d_name, &sb,
399 			    AT_SYMLINK_NOFOLLOW) < 0)
400 				printf("stat(%s): %s\n", dent->d_name,
401 				    strerror(errno));
402 			else
403 				ls(dent->d_name, &sb);
404 		}
405 		closedir(dir);
406 
407 		fchdir(oldcwd);
408 		close(oldcwd);
409 	}
410 
411 out:
412 	disk_close();
413 	return 0;
414 }
415 
416 #define lsrwx(mode,s) \
417 	putchar ((mode) & S_IROTH? 'r' : '-'); \
418 	putchar ((mode) & S_IWOTH? 'w' : '-'); \
419 	putchar ((mode) & S_IXOTH? *(s): (s)[1]);
420 
421 static void
422 ls(const char *name, struct stat *sb)
423 {
424 	putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]);
425 	lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-"));
426 	lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-"));
427 	lsrwx(sb->st_mode     , (sb->st_mode & S_ISTXT? "tT" : "x-"));
428 
429 	printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid,
430 	    (u_long)sb->st_size, name);
431 }
432 #undef lsrwx
433 
434 int doboot = 1;
435 
436 static int
437 Xnop(void)
438 {
439 	if (doboot) {
440 		doboot = 0;
441 		return (Xboot());
442 	}
443 
444 	return 0;
445 }
446 
447 static int
448 Xboot(void)
449 {
450 	if (cmd.argc > 1 && cmd.argv[1][0] != '-') {
451 		qualify((cmd.argv[1]? cmd.argv[1]: cmd.image));
452 		if (bootparse(2))
453 			return 0;
454 	} else {
455 		if (bootparse(1))
456 			return 0;
457 		snprintf(cmd.path, sizeof cmd.path, "%s:%s",
458 		    cmd.bootdev, cmd.image);
459 	}
460 
461 	return 1;
462 }
463 
464 /*
465  * Qualifies the path adding necessary dev
466  */
467 
468 static char *
469 qualify(char *name)
470 {
471 	char *p;
472 
473 	for (p = name; *p; p++)
474 		if (*p == ':')
475 			break;
476 	if (*p == ':')
477 		strlcpy(cmd.path, name, sizeof(cmd.path));
478 	else
479 		snprintf(cmd.path, sizeof cmd.path, "%s:%s",
480 		    cmd.bootdev, name);
481 	return cmd.path;
482 }
483 
484 static int
485 Xreboot(void)
486 {
487 	printf("Rebooting...\n");
488 	reboot(0);
489 	return 0; /* just in case */
490 }
491 
492 int
493 upgrade(void)
494 {
495 	struct stat sb;
496 	const char *path;
497 	int ret = 0;
498 
499 	path = disk_open(qualify("/bsd.upgrade"));
500 	if (path == NULL)
501 		return 0;
502 	if (stat(path, &sb) == 0 && S_ISREG(sb.st_mode))
503 		ret = 1;
504 	disk_close();
505 
506 	return ret;
507 }
508