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