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