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