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