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 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 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 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 * 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 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 * 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 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 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 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 377 Xmachine(void) 378 { 379 printf("machine:"); 380 print_help(MACHINE_CMD); 381 return 0; 382 } 383 #endif 384 385 static int 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 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 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 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 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 498 Xnop(void) 499 { 500 if (doboot) { 501 doboot = 0; 502 return (Xboot()); 503 } 504 505 return 0; 506 } 507 508 static int 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 * 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 546 Xreboot(void) 547 { 548 printf("Rebooting...\n"); 549 exit(); 550 return 0; /* just in case */ 551 } 552 553 int 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