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