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