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