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