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