1 /* $OpenBSD: exec.c,v 1.15 2009/10/27 23:59:21 deraadt Exp $ */ 2 /* $NetBSD: exec.c,v 1.9 1996/09/30 20:03:54 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <dirent.h> 36 #include <fcntl.h> 37 #include <sys/stat.h> 38 #include <errno.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <stdarg.h> 43 44 #include "csh.h" 45 #include "extern.h" 46 47 /* 48 * System level search and execute of a command. We look in each directory 49 * for the specified command name. If the name contains a '/' then we 50 * execute only the full path name. If there is no search path then we 51 * execute only full path names. 52 */ 53 extern char **environ; 54 55 /* 56 * As we search for the command we note the first non-trivial error 57 * message for presentation to the user. This allows us often 58 * to show that a file has the wrong mode/no access when the file 59 * is not in the last component of the search path, so we must 60 * go on after first detecting the error. 61 */ 62 static char *exerr; /* Execution error message */ 63 static Char *expath; /* Path for exerr */ 64 65 /* 66 * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used 67 * to hash execs. If it is allocated (havhash true), then to tell 68 * whether ``name'' is (possibly) present in the i'th component 69 * of the variable path, you look at the bit in xhash indexed by 70 * hash(hashname("name"), i). This is setup automatically 71 * after .login is executed, and recomputed whenever ``path'' is 72 * changed. 73 * The two part hash function is designed to let texec() call the 74 * more expensive hashname() only once and the simple hash() several 75 * times (once for each path component checked). 76 * Byte size is assumed to be 8. 77 */ 78 #define HSHSIZ 8192 /* 1k bytes */ 79 #define HSHMASK (HSHSIZ - 1) 80 #define HSHMUL 243 81 static char xhash[HSHSIZ / 8]; 82 83 #define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK) 84 #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ 85 #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ 86 static int hits, misses; 87 88 /* Dummy search path for just absolute search when no path */ 89 static Char *justabs[] = {STRNULL, 0}; 90 91 static void pexerr(void); 92 static void texec(Char *, Char **); 93 static int hashname(Char *); 94 static int tellmewhat(struct wordent *, Char *, int len); 95 static int executable(Char *, Char *, bool); 96 static int iscommand(Char *); 97 98 99 void 100 /*ARGSUSED*/ 101 doexec(Char **v, struct command *t) 102 { 103 Char *dp, **pv, **av, *sav; 104 struct varent *pathv; 105 bool slash; 106 int hashval = 0, hashval1, i; 107 Char *blk[2]; 108 sigset_t sigset; 109 110 /* 111 * Glob the command name. We will search $path even if this does something, 112 * as in sh but not in csh. One special case: if there is no PATH, then we 113 * execute only commands which start with '/'. 114 */ 115 blk[0] = t->t_dcom[0]; 116 blk[1] = 0; 117 gflag = 0, tglob(blk); 118 if (gflag) { 119 pv = globall(blk); 120 if (pv == 0) { 121 setname(vis_str(blk[0])); 122 stderror(ERR_NAME | ERR_NOMATCH); 123 } 124 gargv = 0; 125 } 126 else 127 pv = saveblk(blk); 128 129 trim(pv); 130 131 exerr = 0; 132 expath = Strsave(pv[0]); 133 Vexpath = expath; 134 135 pathv = adrof(STRpath); 136 if (pathv == 0 && expath[0] != '/') { 137 blkfree(pv); 138 pexerr(); 139 } 140 slash = any(short2str(expath), '/'); 141 142 /* 143 * Glob the argument list, if necessary. Otherwise trim off the quote bits. 144 */ 145 gflag = 0; 146 av = &t->t_dcom[1]; 147 tglob(av); 148 if (gflag) { 149 av = globall(av); 150 if (av == 0) { 151 blkfree(pv); 152 setname(vis_str(expath)); 153 stderror(ERR_NAME | ERR_NOMATCH); 154 } 155 gargv = 0; 156 } 157 else 158 av = saveblk(av); 159 160 blkfree(t->t_dcom); 161 t->t_dcom = blkspl(pv, av); 162 xfree((ptr_t) pv); 163 xfree((ptr_t) av); 164 av = t->t_dcom; 165 trim(av); 166 167 if (*av == NULL || **av == '\0') 168 pexerr(); 169 170 xechoit(av); /* Echo command if -x */ 171 /* 172 * Since all internal file descriptors are set to close on exec, we don't 173 * need to close them explicitly here. Just reorient ourselves for error 174 * messages. 175 */ 176 SHIN = 0; 177 SHOUT = 1; 178 SHERR = 2; 179 OLDSTD = 0; 180 /* 181 * We must do this AFTER any possible forking (like `foo` in glob) so that 182 * this shell can still do subprocesses. 183 */ 184 sigemptyset(&sigset); 185 sigprocmask(SIG_SETMASK, &sigset, NULL); 186 /* 187 * If no path, no words in path, or a / in the filename then restrict the 188 * command search. 189 */ 190 if (pathv == 0 || pathv->vec[0] == 0 || slash) 191 pv = justabs; 192 else 193 pv = pathv->vec; 194 sav = Strspl(STRslash, *av);/* / command name for postpending */ 195 Vsav = sav; 196 if (havhash) 197 hashval = hashname(*av); 198 i = 0; 199 hits++; 200 do { 201 /* 202 * Try to save time by looking at the hash table for where this command 203 * could be. If we are doing delayed hashing, then we put the names in 204 * one at a time, as the user enters them. This is kinda like Korn 205 * Shell's "tracked aliases". 206 */ 207 if (!slash && pv[0][0] == '/' && havhash) { 208 hashval1 = hash(hashval, i); 209 if (!bit(xhash, hashval1)) 210 goto cont; 211 } 212 if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ 213 texec(*av, av); 214 else { 215 dp = Strspl(*pv, sav); 216 Vdp = dp; 217 texec(dp, av); 218 Vdp = 0; 219 xfree((ptr_t) dp); 220 } 221 misses++; 222 cont: 223 pv++; 224 i++; 225 } while (*pv); 226 hits--; 227 Vsav = 0; 228 xfree((ptr_t) sav); 229 pexerr(); 230 } 231 232 static void 233 pexerr(void) 234 { 235 /* Couldn't find the damn thing */ 236 if (expath) { 237 setname(vis_str(expath)); 238 Vexpath = 0; 239 xfree((ptr_t) expath); 240 expath = 0; 241 } 242 else 243 setname(""); 244 if (exerr) 245 stderror(ERR_NAME | ERR_STRING, exerr); 246 stderror(ERR_NAME | ERR_COMMAND); 247 } 248 249 /* 250 * Execute command f, arg list t. 251 * Record error message if not found. 252 * Also do shell scripts here. 253 */ 254 static void 255 texec(Char *sf, Char **st) 256 { 257 char **t; 258 char *f; 259 struct varent *v; 260 Char **vp; 261 Char *lastsh[2]; 262 int fd; 263 unsigned char c; 264 Char *st0, **ost; 265 266 /* The order for the conversions is significant */ 267 t = short2blk(st); 268 f = short2str(sf); 269 Vt = t; 270 errno = 0; /* don't use a previous error */ 271 (void) execve(f, t, environ); 272 Vt = 0; 273 blkfree((Char **) t); 274 switch (errno) { 275 276 case ENOEXEC: 277 /* 278 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute 279 * it, don't feed it to the shell if it looks like a binary! 280 */ 281 if ((fd = open(f, O_RDONLY)) != -1) { 282 if (read(fd, (char *) &c, 1) == 1) { 283 if (!Isprint(c) && (c != '\n' && c != '\t')) { 284 (void) close(fd); 285 /* 286 * We *know* what ENOEXEC means. 287 */ 288 stderror(ERR_ARCH, f, strerror(errno)); 289 } 290 } 291 #ifdef _PATH_BSHELL 292 else 293 c = '#'; 294 #endif 295 (void) close(fd); 296 } 297 /* 298 * If there is an alias for shell, then put the words of the alias in 299 * front of the argument list replacing the command name. Note no 300 * interpretation of the words at this point. 301 */ 302 v = adrof1(STRshell, &aliases); 303 if (v == 0) { 304 vp = lastsh; 305 vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; 306 vp[1] = NULL; 307 #ifdef _PATH_BSHELL 308 if (fd != -1 && c != '#') 309 vp[0] = STR_BSHELL; 310 #endif 311 } 312 else 313 vp = v->vec; 314 st0 = st[0]; 315 st[0] = sf; 316 ost = st; 317 st = blkspl(vp, st); /* Splice up the new arglst */ 318 ost[0] = st0; 319 sf = *st; 320 /* The order for the conversions is significant */ 321 t = short2blk(st); 322 f = short2str(sf); 323 xfree((ptr_t) st); 324 Vt = t; 325 (void) execve(f, t, environ); 326 Vt = 0; 327 blkfree((Char **) t); 328 /* The sky is falling, the sky is falling! */ 329 330 case ENOMEM: 331 stderror(ERR_SYSTEM, f, strerror(errno)); 332 333 case ENOENT: 334 break; 335 336 default: 337 if (exerr == 0) { 338 exerr = strerror(errno); 339 if (expath) 340 xfree((ptr_t) expath); 341 expath = Strsave(sf); 342 Vexpath = expath; 343 } 344 } 345 } 346 347 /*ARGSUSED*/ 348 void 349 execash(Char **t, struct command *kp) 350 { 351 int saveIN, saveOUT, saveDIAG, saveSTD; 352 int oSHIN; 353 int oSHOUT; 354 int oSHERR; 355 int oOLDSTD; 356 jmp_buf osetexit; 357 int my_reenter; 358 int odidfds; 359 sig_t osigint, osigquit, osigterm; 360 361 if (chkstop == 0 && setintr) 362 panystop(0); 363 /* 364 * Hmm, we don't really want to do that now because we might 365 * fail, but what is the choice 366 */ 367 rechist(); 368 369 osigint = signal(SIGINT, parintr); 370 osigquit = signal(SIGQUIT, parintr); 371 osigterm = signal(SIGTERM, parterm); 372 373 odidfds = didfds; 374 oSHIN = SHIN; 375 oSHOUT = SHOUT; 376 oSHERR = SHERR; 377 oOLDSTD = OLDSTD; 378 379 saveIN = dcopy(SHIN, -1); 380 saveOUT = dcopy(SHOUT, -1); 381 saveDIAG = dcopy(SHERR, -1); 382 saveSTD = dcopy(OLDSTD, -1); 383 384 lshift(kp->t_dcom, 1); 385 386 getexit(osetexit); 387 388 if ((my_reenter = setexit()) == 0) { 389 SHIN = dcopy(0, -1); 390 SHOUT = dcopy(1, -1); 391 SHERR = dcopy(2, -1); 392 didfds = 0; 393 doexec(t, kp); 394 } 395 396 (void) signal(SIGINT, osigint); 397 (void) signal(SIGQUIT, osigquit); 398 (void) signal(SIGTERM, osigterm); 399 400 doneinp = 0; 401 didfds = odidfds; 402 (void) close(SHIN); 403 (void) close(SHOUT); 404 (void) close(SHERR); 405 (void) close(OLDSTD); 406 SHIN = dmove(saveIN, oSHIN); 407 SHOUT = dmove(saveOUT, oSHOUT); 408 SHERR = dmove(saveDIAG, oSHERR); 409 OLDSTD = dmove(saveSTD, oOLDSTD); 410 411 resexit(osetexit); 412 if (my_reenter) 413 stderror(ERR_SILENT); 414 } 415 416 void 417 xechoit(Char **t) 418 { 419 if (adrof(STRecho)) { 420 (void) fflush(csherr); 421 blkpr(csherr, t); 422 (void) fputc('\n', csherr); 423 } 424 } 425 426 void 427 /*ARGSUSED*/ 428 dohash(Char **v, struct command *t) 429 { 430 DIR *dirp; 431 struct dirent *dp; 432 int cnt; 433 int i = 0; 434 struct varent *pathv = adrof(STRpath); 435 Char **pv; 436 int hashval; 437 438 havhash = 1; 439 for (cnt = 0; cnt < sizeof xhash; cnt++) 440 xhash[cnt] = 0; 441 if (pathv == 0) 442 return; 443 for (pv = pathv->vec; *pv; pv++, i++) { 444 if (pv[0][0] != '/') 445 continue; 446 dirp = opendir(short2str(*pv)); 447 if (dirp == NULL) 448 continue; 449 while ((dp = readdir(dirp)) != NULL) { 450 if (dp->d_ino == 0) 451 continue; 452 if (dp->d_name[0] == '.' && 453 (dp->d_name[1] == '\0' || 454 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 455 continue; 456 hashval = hash(hashname(str2short(dp->d_name)), i); 457 bis(xhash, hashval); 458 /* tw_add_comm_name (dp->d_name); */ 459 } 460 (void) closedir(dirp); 461 } 462 } 463 464 void 465 /*ARGSUSED*/ 466 dounhash(Char **v, struct command *t) 467 { 468 havhash = 0; 469 } 470 471 void 472 /*ARGSUSED*/ 473 hashstat(Char **v, struct command *t) 474 { 475 if (hits + misses) 476 (void) fprintf(cshout, "%d hits, %d misses, %d%%\n", 477 hits, misses, 100 * hits / (hits + misses)); 478 } 479 480 /* 481 * Hash a command name. 482 */ 483 static int 484 hashname(Char *cp) 485 { 486 long h = 0; 487 488 while (*cp) 489 h = hash(h, *cp++); 490 return ((int) h); 491 } 492 493 static int 494 iscommand(Char *name) 495 { 496 Char **pv; 497 Char *sav; 498 struct varent *v; 499 bool slash = any(short2str(name), '/'); 500 int hashval = 0, hashval1, i; 501 502 v = adrof(STRpath); 503 if (v == 0 || v->vec[0] == 0 || slash) 504 pv = justabs; 505 else 506 pv = v->vec; 507 sav = Strspl(STRslash, name); /* / command name for postpending */ 508 if (havhash) 509 hashval = hashname(name); 510 i = 0; 511 do { 512 if (!slash && pv[0][0] == '/' && havhash) { 513 hashval1 = hash(hashval, i); 514 if (!bit(xhash, hashval1)) 515 goto cont; 516 } 517 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */ 518 if (executable(NULL, name, 0)) { 519 xfree((ptr_t) sav); 520 return i + 1; 521 } 522 } 523 else { 524 if (executable(*pv, sav, 0)) { 525 xfree((ptr_t) sav); 526 return i + 1; 527 } 528 } 529 cont: 530 pv++; 531 i++; 532 } while (*pv); 533 xfree((ptr_t) sav); 534 return 0; 535 } 536 537 /* Also by: 538 * Andreas Luik <luik@isaak.isa.de> 539 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 540 * Azenberstr. 35 541 * D-7000 Stuttgart 1 542 * West-Germany 543 * is the executable() routine below and changes to iscommand(). 544 * Thanks again!! 545 */ 546 547 /* 548 * executable() examines the pathname obtained by concatenating dir and name 549 * (dir may be NULL), and returns 1 either if it is executable by us, or 550 * if dir_ok is set and the pathname refers to a directory. 551 * This is a bit kludgy, but in the name of optimization... 552 */ 553 static int 554 executable(Char *dir, Char *name, bool dir_ok) 555 { 556 struct stat stbuf; 557 Char path[MAXPATHLEN], *dp, *sp; 558 char *strname; 559 560 if (dir && *dir) { 561 for (dp = path, sp = dir; *sp; *dp++ = *sp++) 562 if (dp == &path[MAXPATHLEN]) { 563 *--dp = '\0'; 564 break; 565 } 566 for (sp = name; *sp; *dp++ = *sp++) 567 if (dp == &path[MAXPATHLEN]) { 568 *--dp = '\0'; 569 break; 570 } 571 *dp = '\0'; 572 strname = short2str(path); 573 } 574 else 575 strname = short2str(name); 576 return (stat(strname, &stbuf) != -1 && 577 ((S_ISREG(stbuf.st_mode) && 578 /* save time by not calling access() in the hopeless case */ 579 (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && 580 access(strname, X_OK) == 0) || 581 (dir_ok && S_ISDIR(stbuf.st_mode)))); 582 } 583 584 /* The dowhich() is by: 585 * Andreas Luik <luik@isaak.isa.de> 586 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 587 * Azenberstr. 35 588 * D-7000 Stuttgart 1 589 * West-Germany 590 * Thanks!! 591 */ 592 /*ARGSUSED*/ 593 void 594 dowhich(Char **v, struct command *c) 595 { 596 struct wordent lex[3]; 597 struct varent *vp; 598 599 lex[0].next = &lex[1]; 600 lex[1].next = &lex[2]; 601 lex[2].next = &lex[0]; 602 603 lex[0].prev = &lex[2]; 604 lex[1].prev = &lex[0]; 605 lex[2].prev = &lex[1]; 606 607 lex[0].word = STRNULL; 608 lex[2].word = STRret; 609 610 while (*++v) { 611 if ((vp = adrof1(*v, &aliases)) != NULL) { 612 (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); 613 blkpr(cshout, vp->vec); 614 (void) fputc('\n', cshout); 615 set(STRstatus, Strsave(STR0)); 616 } 617 else { 618 lex[1].word = *v; 619 set(STRstatus, Strsave(tellmewhat(lex, NULL, 0) ? STR0 : STR1)); 620 } 621 } 622 } 623 624 static int 625 tellmewhat(struct wordent *lexp, Char *str, int len) 626 { 627 int i; 628 struct biltins *bptr; 629 struct wordent *sp = lexp->next; 630 bool aliased = 0, found; 631 Char *s0, *s1, *s2, *cmd; 632 Char qc; 633 634 if (adrof1(sp->word, &aliases)) { 635 alias(lexp); 636 sp = lexp->next; 637 aliased = 1; 638 } 639 640 s0 = sp->word; /* to get the memory freeing right... */ 641 642 /* handle quoted alias hack */ 643 if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) 644 (sp->word)++; 645 646 /* do quoting, if it hasn't been done */ 647 s1 = s2 = sp->word; 648 while (*s2) 649 switch (*s2) { 650 case '\'': 651 case '"': 652 qc = *s2++; 653 while (*s2 && *s2 != qc) 654 *s1++ = *s2++ | QUOTE; 655 if (*s2) 656 s2++; 657 break; 658 case '\\': 659 if (*++s2) 660 *s1++ = *s2++ | QUOTE; 661 break; 662 default: 663 *s1++ = *s2++; 664 } 665 *s1 = '\0'; 666 667 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { 668 if (eq(sp->word, str2short(bptr->bname))) { 669 if (str == NULL) { 670 if (aliased) 671 prlex(cshout, lexp); 672 (void) fprintf(cshout, "%s: shell built-in command.\n", 673 vis_str(sp->word)); 674 } 675 else 676 (void) Strlcpy(str, sp->word, len/sizeof(Char)); 677 sp->word = s0; /* we save and then restore this */ 678 return 1; 679 } 680 } 681 682 sp->word = cmd = globone(sp->word, G_IGNORE); 683 684 if ((i = iscommand(sp->word)) != 0) { 685 Char **pv; 686 struct varent *v; 687 bool slash = any(short2str(sp->word), '/'); 688 689 v = adrof(STRpath); 690 if (v == 0 || v->vec[0] == 0 || slash) 691 pv = justabs; 692 else 693 pv = v->vec; 694 695 while (--i) 696 pv++; 697 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { 698 if (!slash) { 699 sp->word = Strspl(STRdotsl, sp->word); 700 prlex(cshout, lexp); 701 xfree((ptr_t) sp->word); 702 } 703 else 704 prlex(cshout, lexp); 705 } 706 else { 707 s1 = Strspl(*pv, STRslash); 708 sp->word = Strspl(s1, sp->word); 709 xfree((ptr_t) s1); 710 if (str == NULL) 711 prlex(cshout, lexp); 712 else 713 (void) Strlcpy(str, sp->word, len/sizeof(Char)); 714 xfree((ptr_t) sp->word); 715 } 716 found = 1; 717 } 718 else { 719 if (str == NULL) { 720 if (aliased) 721 prlex(cshout, lexp); 722 (void) fprintf(csherr, 723 "%s: Command not found.\n", vis_str(sp->word)); 724 } 725 else 726 (void) Strlcpy(str, sp->word, len/sizeof(Char)); 727 found = 0; 728 } 729 sp->word = s0; /* we save and then restore this */ 730 xfree((ptr_t) cmd); 731 return found; 732 } 733