1 /* $OpenBSD: cmd.c,v 1.3 2019/08/01 04:52:56 visa 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 #include <sys/select.h> 32 #include <sys/stat.h> 33 34 #include <dirent.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <termios.h> 40 #include <unistd.h> 41 42 #include "cmd.h" 43 #include "disk.h" 44 45 static int Xboot(void); 46 static int Xecho(void); 47 static int Xhelp(void); 48 static int Xls(void); 49 static int Xnop(void); 50 static int Xreboot(void); 51 #ifdef MACHINE_CMD 52 static int Xmachine(void); 53 extern const struct cmd_table MACHINE_CMD[]; 54 #endif 55 extern int Xset(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 {"help", CMDT_CMD, Xhelp}, 67 {"ls", CMDT_CMD, Xls}, 68 #ifdef MACHINE_CMD 69 {"machine",CMDT_MDC, Xmachine}, 70 #endif 71 {"reboot", CMDT_CMD, Xreboot}, 72 {"set", CMDT_SET, Xset}, 73 {NULL, 0}, 74 }; 75 76 static void ls(const 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 struct stat sb; 99 const char *path; 100 int fd, rc = 0; 101 102 #ifdef CHECK_SKIP_CONF 103 if (CHECK_SKIP_CONF()) { 104 printf("boot.conf processing skipped at operator request\n"); 105 cmd.timeout = 0; 106 return -1; /* Pretend file wasn't found */ 107 } 108 #endif 109 110 path = disk_open(qualify(cmd.conf)); 111 if (path == NULL) { 112 fprintf(stderr, "cannot open device for reading %s: %s\n", 113 cmd.conf, strerror(errno)); 114 return -1; 115 } 116 if ((fd = open(path, O_RDONLY)) == -1) { 117 if (errno != ENOENT && errno != ENXIO) { 118 fprintf(stderr, "%s: open(%s): %s\n", __func__, 119 cmd.path, strerror(errno)); 120 rc = 0; 121 } else 122 rc = -1; 123 goto out; 124 } 125 126 (void) fstat(fd, &sb); 127 if (sb.st_uid || (sb.st_mode & 2)) { 128 fprintf(stderr, "non-secure %s, will not proceed\n", cmd.path); 129 rc = -1; 130 goto out; 131 } 132 133 do { 134 char *p = cmd_buf; 135 136 cmd.cmd = NULL; 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 fprintf(stderr, "%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 fprintf(stderr, "%s: line too long\n", 158 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 out: 168 if (fd != -1) 169 close(fd); 170 disk_close(); 171 return rc; 172 } 173 174 int 175 docmd(void) 176 { 177 char *p = NULL; 178 const struct cmd_table *ct = cmd_table, *cs; 179 180 cmd.argc = 1; 181 if (cmd.cmd == NULL) { 182 183 /* command */ 184 for (p = cmd_buf; *p == ' ' || *p == '\t'; p++) 185 ; 186 if (*p == '#' || *p == '\0') { /* comment or empty string */ 187 #ifdef DEBUG 188 printf("rem\n"); 189 #endif 190 return 0; 191 } 192 ct = cmd_table; 193 cs = NULL; 194 cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */ 195 p = whatcmd(&ct, p); 196 if (ct == NULL) { 197 cmd.argc++; 198 ct = cmd_table; 199 } else if (ct->cmd_type == CMDT_SET && p != NULL) { 200 cs = cmd_set; 201 #ifdef MACHINE_CMD 202 } else if (ct->cmd_type == CMDT_MDC && p != NULL) { 203 cs = MACHINE_CMD; 204 #endif 205 } 206 207 if (cs != NULL) { 208 p = whatcmd(&cs, p); 209 if (cs == NULL) { 210 printf("%s: syntax error\n", ct->cmd_name); 211 return 0; 212 } 213 ct = cs; 214 } 215 cmd.cmd = ct; 216 } 217 218 cmd.argv[0] = ct->cmd_name; 219 while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) { 220 cmd.argv[cmd.argc++] = p; 221 p = nextword(p); 222 } 223 cmd.argv[cmd.argc] = NULL; 224 225 return (*cmd.cmd->cmd_exec)(); 226 } 227 228 static char * 229 whatcmd(const struct cmd_table **ct, char *p) 230 { 231 char *q; 232 int l; 233 234 q = nextword(p); 235 236 for (l = 0; p[l]; l++) 237 ; 238 239 while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l)) 240 (*ct)++; 241 242 if ((*ct)->cmd_name == NULL) 243 *ct = NULL; 244 245 return q; 246 } 247 248 static int 249 readline(char *buf, size_t n, int to) 250 { 251 struct termios saved_tio, tio; 252 struct timeval tv; 253 fd_set fdset; 254 char *p; 255 int timed_out = 0; 256 #ifdef DEBUG 257 extern int debug; 258 #endif 259 260 /* Only do timeout if greater than 0 */ 261 if (to > 0) { 262 /* Switch to non-canonical mode for timeout detection. */ 263 tcgetattr(STDIN_FILENO, &saved_tio); 264 tio = saved_tio; 265 tio.c_lflag &= ~(ECHO | ICANON); 266 tcsetattr(STDIN_FILENO, TCSANOW, &tio); 267 268 FD_ZERO(&fdset); 269 FD_SET(STDIN_FILENO, &fdset); 270 tv.tv_sec = to; 271 tv.tv_usec = 0; 272 if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &tv) == 0) 273 timed_out = 1; 274 275 /* Restore canonical mode. */ 276 tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio); 277 278 if (timed_out) { 279 strlcpy(buf, "boot", 5); 280 putchar('\n'); 281 return strlen(buf); 282 } 283 } 284 285 /* User has typed something. Turn off timeouts. */ 286 cmd.timeout = 0; 287 288 if (fgets(buf, n, stdin) == NULL) 289 return 0; 290 291 /* Strip trailing newline. */ 292 p = strchr(buf, '\n'); 293 if (p != NULL) 294 *p = '\0'; 295 296 return strlen(buf); 297 } 298 299 /* 300 * Search for spaces/tabs after the current word. If found, \0 the 301 * first one. Then pass a pointer to the first character of the 302 * next word, or NULL if there is no next word. 303 */ 304 char * 305 nextword(char *p) 306 { 307 /* skip blanks */ 308 while (*p && *p != '\t' && *p != ' ') 309 p++; 310 if (*p) { 311 *p++ = '\0'; 312 while (*p == '\t' || *p == ' ') 313 p++; 314 } 315 if (*p == '\0') 316 p = NULL; 317 return p; 318 } 319 320 static void 321 print_help(const struct cmd_table *ct) 322 { 323 for (; ct->cmd_name != NULL; ct++) 324 printf(" %s", ct->cmd_name); 325 putchar('\n'); 326 } 327 328 static int 329 Xhelp(void) 330 { 331 printf("commands:"); 332 print_help(cmd_table); 333 #ifdef MACHINE_CMD 334 return Xmachine(); 335 #else 336 return 0; 337 #endif 338 } 339 340 #ifdef MACHINE_CMD 341 static int 342 Xmachine(void) 343 { 344 printf("machine:"); 345 print_help(MACHINE_CMD); 346 return 0; 347 } 348 #endif 349 350 static int 351 Xecho(void) 352 { 353 int i; 354 355 for (i = 1; i < cmd.argc; i++) 356 printf("%s ", cmd.argv[i]); 357 putchar('\n'); 358 return 0; 359 } 360 361 static int 362 Xls(void) 363 { 364 struct stat sb; 365 const char *path; 366 DIR *dir; 367 struct dirent *dent; 368 int dirfd, oldcwd; 369 370 path = disk_open(qualify(cmd.argv[1] ? cmd.argv[1] : "/.")); 371 if (path == NULL) 372 return 0; 373 374 if (stat(path, &sb) < 0) { 375 printf("stat(%s): %s\n", cmd.path, strerror(errno)); 376 goto out; 377 } 378 379 if ((sb.st_mode & S_IFMT) != S_IFDIR) 380 ls(path, &sb); 381 else { 382 oldcwd = open(".", O_RDONLY); 383 384 dirfd = open(path, O_RDONLY); 385 if (dirfd < 0) { 386 printf("opendir(%s): %s\n", cmd.path, strerror(errno)); 387 close(oldcwd); 388 goto out; 389 } 390 if ((dir = fdopendir(dirfd)) < 0) { 391 printf("opendir(%s): %s\n", cmd.path, strerror(errno)); 392 close(dirfd); 393 close(oldcwd); 394 goto out; 395 } 396 fchdir(dirfd); 397 while ((dent = readdir(dir)) != NULL) { 398 if (fstatat(dirfd, dent->d_name, &sb, 399 AT_SYMLINK_NOFOLLOW) < 0) 400 printf("stat(%s): %s\n", dent->d_name, 401 strerror(errno)); 402 else 403 ls(dent->d_name, &sb); 404 } 405 closedir(dir); 406 407 fchdir(oldcwd); 408 close(oldcwd); 409 } 410 411 out: 412 disk_close(); 413 return 0; 414 } 415 416 #define lsrwx(mode,s) \ 417 putchar ((mode) & S_IROTH? 'r' : '-'); \ 418 putchar ((mode) & S_IWOTH? 'w' : '-'); \ 419 putchar ((mode) & S_IXOTH? *(s): (s)[1]); 420 421 static void 422 ls(const char *name, struct stat *sb) 423 { 424 putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]); 425 lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-")); 426 lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-")); 427 lsrwx(sb->st_mode , (sb->st_mode & S_ISTXT? "tT" : "x-")); 428 429 printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid, 430 (u_long)sb->st_size, name); 431 } 432 #undef lsrwx 433 434 int doboot = 1; 435 436 static int 437 Xnop(void) 438 { 439 if (doboot) { 440 doboot = 0; 441 return (Xboot()); 442 } 443 444 return 0; 445 } 446 447 static int 448 Xboot(void) 449 { 450 if (cmd.argc > 1 && cmd.argv[1][0] != '-') { 451 qualify((cmd.argv[1]? cmd.argv[1]: cmd.image)); 452 if (bootparse(2)) 453 return 0; 454 } else { 455 if (bootparse(1)) 456 return 0; 457 snprintf(cmd.path, sizeof cmd.path, "%s:%s", 458 cmd.bootdev, cmd.image); 459 } 460 461 return 1; 462 } 463 464 /* 465 * Qualifies the path adding necessary dev 466 */ 467 468 static char * 469 qualify(char *name) 470 { 471 char *p; 472 473 for (p = name; *p; p++) 474 if (*p == ':') 475 break; 476 if (*p == ':') 477 strlcpy(cmd.path, name, sizeof(cmd.path)); 478 else 479 snprintf(cmd.path, sizeof cmd.path, "%s:%s", 480 cmd.bootdev, name); 481 return cmd.path; 482 } 483 484 static int 485 Xreboot(void) 486 { 487 printf("Rebooting...\n"); 488 reboot(0); 489 return 0; /* just in case */ 490 } 491 492 int 493 upgrade(void) 494 { 495 struct stat sb; 496 const char *path; 497 int ret = 0; 498 499 path = disk_open(qualify("/bsd.upgrade")); 500 if (path == NULL) 501 return 0; 502 if (stat(path, &sb) == 0 && S_ISREG(sb.st_mode)) 503 ret = 1; 504 disk_close(); 505 506 return ret; 507 } 508