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