1 /* $NetBSD: exec.c,v 1.15 1998/07/28 11:41:43 mycroft 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.15 1998/07/28 11:41:43 mycroft 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 const 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)) __attribute__((noreturn)); 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 (void) 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 /* NOTREACHED */ 249 } 250 251 static void 252 pexerr() 253 { 254 /* Couldn't find the damn thing */ 255 if (expath) { 256 setname(vis_str(expath)); 257 Vexpath = 0; 258 xfree((ptr_t) expath); 259 expath = 0; 260 } 261 else 262 setname(""); 263 if (exerr) 264 stderror(ERR_NAME | ERR_STRING, exerr); 265 else 266 stderror(ERR_NAME | ERR_COMMAND); 267 /* NOTREACHED */ 268 } 269 270 /* 271 * Execute command f, arg list t. 272 * Record error message if not found. 273 * Also do shell scripts here. 274 */ 275 static void 276 texec(sf, st) 277 Char *sf; 278 Char **st; 279 { 280 char **t; 281 char *f; 282 struct varent *v; 283 Char **vp; 284 Char *lastsh[2]; 285 int fd; 286 unsigned char c; 287 Char *st0, **ost; 288 289 /* The order for the conversions is significant */ 290 t = short2blk(st); 291 f = short2str(sf); 292 Vt = t; 293 errno = 0; /* don't use a previous error */ 294 (void) execve(f, t, environ); 295 Vt = 0; 296 blkfree((Char **) t); 297 switch (errno) { 298 299 case ENOEXEC: 300 /* 301 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute 302 * it, don't feed it to the shell if it looks like a binary! 303 */ 304 if ((fd = open(f, O_RDONLY)) != -1) { 305 if (read(fd, (char *) &c, 1) == 1) { 306 if (!Isprint(c) && (c != '\n' && c != '\t')) { 307 (void) close(fd); 308 /* 309 * We *know* what ENOEXEC means. 310 */ 311 stderror(ERR_ARCH, f, strerror(errno)); 312 } 313 } 314 #ifdef _PATH_BSHELL 315 else 316 c = '#'; 317 #endif 318 (void) close(fd); 319 } 320 /* 321 * If there is an alias for shell, then put the words of the alias in 322 * front of the argument list replacing the command name. Note no 323 * interpretation of the words at this point. 324 */ 325 v = adrof1(STRshell, &aliases); 326 if (v == 0) { 327 vp = lastsh; 328 vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; 329 vp[1] = NULL; 330 #ifdef _PATH_BSHELL 331 if (fd != -1 && c != '#') 332 vp[0] = STR_BSHELL; 333 #endif 334 } 335 else 336 vp = v->vec; 337 st0 = st[0]; 338 st[0] = sf; 339 ost = st; 340 st = blkspl(vp, st); /* Splice up the new arglst */ 341 ost[0] = st0; 342 sf = *st; 343 /* The order for the conversions is significant */ 344 t = short2blk(st); 345 f = short2str(sf); 346 xfree((ptr_t) st); 347 Vt = t; 348 (void) execve(f, t, environ); 349 Vt = 0; 350 blkfree((Char **) t); 351 /* FALLTHROUGH */ 352 353 case ENOMEM: 354 stderror(ERR_SYSTEM, f, strerror(errno)); 355 /* NOTREACHED */ 356 357 case ENOENT: 358 break; 359 360 default: 361 if (exerr == 0) { 362 exerr = strerror(errno); 363 if (expath) 364 xfree((ptr_t) expath); 365 expath = Strsave(sf); 366 Vexpath = expath; 367 } 368 } 369 } 370 371 /*ARGSUSED*/ 372 void 373 execash(t, kp) 374 Char **t; 375 struct command *kp; 376 { 377 int saveIN, saveOUT, saveDIAG, saveSTD; 378 int oSHIN; 379 int oSHOUT; 380 int oSHERR; 381 int oOLDSTD; 382 jmp_buf osetexit; 383 int my_reenter; 384 int odidfds; 385 sig_t osigint, osigquit, osigterm; 386 387 if (chkstop == 0 && setintr) 388 panystop(0); 389 /* 390 * Hmm, we don't really want to do that now because we might 391 * fail, but what is the choice 392 */ 393 rechist(); 394 395 osigint = signal(SIGINT, parintr); 396 osigquit = signal(SIGQUIT, parintr); 397 osigterm = signal(SIGTERM, parterm); 398 399 odidfds = didfds; 400 oSHIN = SHIN; 401 oSHOUT = SHOUT; 402 oSHERR = SHERR; 403 oOLDSTD = OLDSTD; 404 405 saveIN = dcopy(SHIN, -1); 406 saveOUT = dcopy(SHOUT, -1); 407 saveDIAG = dcopy(SHERR, -1); 408 saveSTD = dcopy(OLDSTD, -1); 409 410 lshift(kp->t_dcom, 1); 411 412 getexit(osetexit); 413 414 if ((my_reenter = setexit()) == 0) { 415 SHIN = dcopy(0, -1); 416 SHOUT = dcopy(1, -1); 417 SHERR = dcopy(2, -1); 418 didfds = 0; 419 doexec(t, kp); 420 } 421 422 (void) signal(SIGINT, osigint); 423 (void) signal(SIGQUIT, osigquit); 424 (void) signal(SIGTERM, osigterm); 425 426 doneinp = 0; 427 didfds = odidfds; 428 (void) close(SHIN); 429 (void) close(SHOUT); 430 (void) close(SHERR); 431 (void) close(OLDSTD); 432 SHIN = dmove(saveIN, oSHIN); 433 SHOUT = dmove(saveOUT, oSHOUT); 434 SHERR = dmove(saveDIAG, oSHERR); 435 OLDSTD = dmove(saveSTD, oOLDSTD); 436 437 resexit(osetexit); 438 if (my_reenter) 439 stderror(ERR_SILENT); 440 } 441 442 void 443 xechoit(t) 444 Char **t; 445 { 446 if (adrof(STRecho)) { 447 (void) fflush(csherr); 448 blkpr(csherr, t); 449 (void) fputc('\n', csherr); 450 } 451 } 452 453 void 454 /*ARGSUSED*/ 455 dohash(v, t) 456 Char **v; 457 struct command *t; 458 { 459 DIR *dirp; 460 struct dirent *dp; 461 int cnt; 462 int i = 0; 463 struct varent *pathv = adrof(STRpath); 464 Char **pv; 465 int hashval; 466 467 havhash = 1; 468 for (cnt = 0; cnt < sizeof xhash; cnt++) 469 xhash[cnt] = 0; 470 if (pathv == 0) 471 return; 472 for (pv = pathv->vec; *pv; pv++, i++) { 473 if (pv[0][0] != '/') 474 continue; 475 dirp = opendir(short2str(*pv)); 476 if (dirp == NULL) 477 continue; 478 while ((dp = readdir(dirp)) != NULL) { 479 if (dp->d_ino == 0) 480 continue; 481 if (dp->d_name[0] == '.' && 482 (dp->d_name[1] == '\0' || 483 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 484 continue; 485 hashval = hash(hashname(str2short(dp->d_name)), i); 486 bis(xhash, hashval); 487 /* tw_add_comm_name (dp->d_name); */ 488 } 489 (void) closedir(dirp); 490 } 491 } 492 493 void 494 /*ARGSUSED*/ 495 dounhash(v, t) 496 Char **v; 497 struct command *t; 498 { 499 havhash = 0; 500 } 501 502 void 503 /*ARGSUSED*/ 504 hashstat(v, t) 505 Char **v; 506 struct command *t; 507 { 508 if (hits + misses) 509 (void) fprintf(cshout, "%d hits, %d misses, %d%%\n", 510 hits, misses, 100 * hits / (hits + misses)); 511 } 512 513 /* 514 * Hash a command name. 515 */ 516 static int 517 hashname(cp) 518 Char *cp; 519 { 520 long h = 0; 521 522 while (*cp) 523 h = hash(h, *cp++); 524 return ((int) h); 525 } 526 527 static int 528 iscommand(name) 529 Char *name; 530 { 531 Char **pv; 532 Char *sav; 533 struct varent *v; 534 bool slash = any(short2str(name), '/'); 535 int hashval = 0, hashval1, i; 536 537 v = adrof(STRpath); 538 if (v == 0 || v->vec[0] == 0 || slash) 539 pv = justabs; 540 else 541 pv = v->vec; 542 sav = Strspl(STRslash, name); /* / command name for postpending */ 543 if (havhash) 544 hashval = hashname(name); 545 i = 0; 546 do { 547 if (!slash && pv[0][0] == '/' && havhash) { 548 hashval1 = hash(hashval, i); 549 if (!bit(xhash, hashval1)) 550 goto cont; 551 } 552 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */ 553 if (executable(NULL, name, 0)) { 554 xfree((ptr_t) sav); 555 return i + 1; 556 } 557 } 558 else { 559 if (executable(*pv, sav, 0)) { 560 xfree((ptr_t) sav); 561 return i + 1; 562 } 563 } 564 cont: 565 pv++; 566 i++; 567 } while (*pv); 568 xfree((ptr_t) sav); 569 return 0; 570 } 571 572 /* Also by: 573 * Andreas Luik <luik@isaak.isa.de> 574 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 575 * Azenberstr. 35 576 * D-7000 Stuttgart 1 577 * West-Germany 578 * is the executable() routine below and changes to iscommand(). 579 * Thanks again!! 580 */ 581 582 /* 583 * executable() examines the pathname obtained by concatenating dir and name 584 * (dir may be NULL), and returns 1 either if it is executable by us, or 585 * if dir_ok is set and the pathname refers to a directory. 586 * This is a bit kludgy, but in the name of optimization... 587 */ 588 static int 589 executable(dir, name, dir_ok) 590 Char *dir, *name; 591 bool dir_ok; 592 { 593 struct stat stbuf; 594 Char path[MAXPATHLEN + 1], *dp, *sp; 595 char *strname; 596 597 if (dir && *dir) { 598 for (dp = path, sp = dir; *sp; *dp++ = *sp++) 599 if (dp == &path[MAXPATHLEN + 1]) { 600 *--dp = '\0'; 601 break; 602 } 603 for (sp = name; *sp; *dp++ = *sp++) 604 if (dp == &path[MAXPATHLEN + 1]) { 605 *--dp = '\0'; 606 break; 607 } 608 *dp = '\0'; 609 strname = short2str(path); 610 } 611 else 612 strname = short2str(name); 613 return (stat(strname, &stbuf) != -1 && 614 ((S_ISREG(stbuf.st_mode) && 615 /* save time by not calling access() in the hopeless case */ 616 (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && 617 access(strname, X_OK) == 0) || 618 (dir_ok && S_ISDIR(stbuf.st_mode)))); 619 } 620 621 /* The dowhich() is by: 622 * Andreas Luik <luik@isaak.isa.de> 623 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 624 * Azenberstr. 35 625 * D-7000 Stuttgart 1 626 * West-Germany 627 * Thanks!! 628 */ 629 /*ARGSUSED*/ 630 void 631 dowhich(v, c) 632 Char **v; 633 struct command *c; 634 { 635 struct wordent lex[3]; 636 struct varent *vp; 637 638 lex[0].next = &lex[1]; 639 lex[1].next = &lex[2]; 640 lex[2].next = &lex[0]; 641 642 lex[0].prev = &lex[2]; 643 lex[1].prev = &lex[0]; 644 lex[2].prev = &lex[1]; 645 646 lex[0].word = STRNULL; 647 lex[2].word = STRret; 648 649 while (*++v) { 650 if ((vp = adrof1(*v, &aliases)) != NULL) { 651 (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); 652 blkpr(cshout, vp->vec); 653 (void) fputc('\n', cshout); 654 set(STRstatus, Strsave(STR0)); 655 } 656 else { 657 lex[1].word = *v; 658 set(STRstatus, Strsave(tellmewhat(lex, NULL) ? STR0 : STR1)); 659 } 660 } 661 } 662 663 static int 664 tellmewhat(lexp, str) 665 struct wordent *lexp; 666 Char *str; 667 { 668 int i; 669 struct biltins *bptr; 670 struct wordent *sp = lexp->next; 671 bool aliased = 0, found; 672 Char *s0, *s1, *s2, *cmd; 673 Char qc; 674 675 if (adrof1(sp->word, &aliases)) { 676 alias(lexp); 677 sp = lexp->next; 678 aliased = 1; 679 } 680 681 s0 = sp->word; /* to get the memory freeing right... */ 682 683 /* handle quoted alias hack */ 684 if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) 685 (sp->word)++; 686 687 /* do quoting, if it hasn't been done */ 688 s1 = s2 = sp->word; 689 while (*s2) 690 switch (*s2) { 691 case '\'': 692 case '"': 693 qc = *s2++; 694 while (*s2 && *s2 != qc) 695 *s1++ = *s2++ | QUOTE; 696 if (*s2) 697 s2++; 698 break; 699 case '\\': 700 if (*++s2) 701 *s1++ = *s2++ | QUOTE; 702 break; 703 default: 704 *s1++ = *s2++; 705 } 706 *s1 = '\0'; 707 708 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { 709 if (eq(sp->word, str2short(bptr->bname))) { 710 if (str == NULL) { 711 if (aliased) 712 prlex(cshout, lexp); 713 (void) fprintf(cshout, "%s: shell built-in command.\n", 714 vis_str(sp->word)); 715 } 716 else 717 (void) Strcpy(str, sp->word); 718 sp->word = s0; /* we save and then restore this */ 719 return 1; 720 } 721 } 722 723 sp->word = cmd = globone(sp->word, G_IGNORE); 724 725 if ((i = iscommand(sp->word)) != 0) { 726 Char **pv; 727 struct varent *v; 728 bool slash = any(short2str(sp->word), '/'); 729 730 v = adrof(STRpath); 731 if (v == 0 || v->vec[0] == 0 || slash) 732 pv = justabs; 733 else 734 pv = v->vec; 735 736 while (--i) 737 pv++; 738 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { 739 if (!slash) { 740 sp->word = Strspl(STRdotsl, sp->word); 741 prlex(cshout, lexp); 742 xfree((ptr_t) sp->word); 743 } 744 else 745 prlex(cshout, lexp); 746 } 747 else { 748 s1 = Strspl(*pv, STRslash); 749 sp->word = Strspl(s1, sp->word); 750 xfree((ptr_t) s1); 751 if (str == NULL) 752 prlex(cshout, lexp); 753 else 754 (void) Strcpy(str, sp->word); 755 xfree((ptr_t) sp->word); 756 } 757 found = 1; 758 } 759 else { 760 if (str == NULL) { 761 if (aliased) 762 prlex(cshout, lexp); 763 (void) fprintf(csherr, 764 "%s: Command not found.\n", vis_str(sp->word)); 765 } 766 else 767 (void) Strcpy(str, sp->word); 768 found = 0; 769 } 770 sp->word = s0; /* we save and then restore this */ 771 xfree((ptr_t) cmd); 772 return found; 773 } 774