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