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