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