1 /* $NetBSD: exec.c,v 1.29 2013/07/16 17:47:43 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. 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.29 2013/07/16 17:47:43 christos 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 unsigned 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] |= (unsigned char)(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(void) __dead; 101 static void texec(Char *, Char **); 102 static int hashname(Char *); 103 static int tellmewhat(struct wordent *, Char *); 104 static int executable(Char *, Char *, int); 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 int 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 = '\0'; 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 size_t cnt; 444 int hashval, i; 445 446 i = 0; 447 havhash = 1; 448 pathv = adrof(STRpath); 449 450 for (cnt = 0; cnt < sizeof xhash; cnt++) 451 xhash[cnt] = 0; 452 if (pathv == 0) 453 return; 454 for (pv = pathv->vec; *pv; pv++, i++) { 455 if (pv[0][0] != '/') 456 continue; 457 dirp = opendir(short2str(*pv)); 458 if (dirp == NULL) 459 continue; 460 while ((dp = readdir(dirp)) != NULL) { 461 if (dp->d_ino == 0) 462 continue; 463 if (dp->d_name[0] == '.' && 464 (dp->d_name[1] == '\0' || 465 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 466 continue; 467 hashval = hash(hashname(str2short(dp->d_name)), i); 468 bis(xhash, hashval); 469 /* tw_add_comm_name (dp->d_name); */ 470 } 471 (void) closedir(dirp); 472 } 473 } 474 475 void 476 /*ARGSUSED*/ 477 dounhash(Char **v, struct command *t) 478 { 479 havhash = 0; 480 } 481 482 void 483 /*ARGSUSED*/ 484 hashstat(Char **v, struct command *t) 485 { 486 if (hits + misses) 487 (void)fprintf(cshout, "%d hits, %d misses, %d%%\n", 488 hits, misses, 100 * hits / (hits + misses)); 489 } 490 491 /* 492 * Hash a command name. 493 */ 494 static int 495 hashname(Char *cp) 496 { 497 long h = 0; 498 499 while (*cp) 500 h = hash(h, *cp++); 501 return ((int) h); 502 } 503 504 static int 505 iscommand(Char *name) 506 { 507 struct varent *v; 508 Char **pv, *sav; 509 int hashval, hashval1, i; 510 int slash; 511 512 hashval = 0; 513 slash = any(short2str(name), '/'); 514 v = adrof(STRpath); 515 516 if (v == 0 || v->vec[0] == 0 || slash) 517 pv = justabs; 518 else 519 pv = v->vec; 520 sav = Strspl(STRslash, name); /* / command name for postpending */ 521 if (havhash) 522 hashval = hashname(name); 523 i = 0; 524 do { 525 if (!slash && pv[0][0] == '/' && havhash) { 526 hashval1 = hash(hashval, i); 527 if (!bit(xhash, hashval1)) 528 goto cont; 529 } 530 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */ 531 if (executable(NULL, name, 0)) { 532 xfree((ptr_t) sav); 533 return i + 1; 534 } 535 } 536 else { 537 if (executable(*pv, sav, 0)) { 538 xfree((ptr_t) sav); 539 return i + 1; 540 } 541 } 542 cont: 543 pv++; 544 i++; 545 } while (*pv); 546 xfree((ptr_t) sav); 547 return 0; 548 } 549 550 /* Also by: 551 * Andreas Luik <luik@isaak.isa.de> 552 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 553 * Azenberstr. 35 554 * D-7000 Stuttgart 1 555 * West-Germany 556 * is the executable() routine below and changes to iscommand(). 557 * Thanks again!! 558 */ 559 560 /* 561 * executable() examines the pathname obtained by concatenating dir and name 562 * (dir may be NULL), and returns 1 either if it is executable by us, or 563 * if dir_ok is set and the pathname refers to a directory. 564 * This is a bit kludgy, but in the name of optimization... 565 */ 566 static int 567 executable(Char *dir, Char *name, int dir_ok) 568 { 569 struct stat stbuf; 570 Char path[MAXPATHLEN + 1], *dp, *sp; 571 char *strname; 572 573 if (dir && *dir) { 574 for (dp = path, sp = dir; *sp; *dp++ = *sp++) 575 if (dp == &path[MAXPATHLEN + 1]) { 576 *--dp = '\0'; 577 break; 578 } 579 for (sp = name; *sp; *dp++ = *sp++) 580 if (dp == &path[MAXPATHLEN + 1]) { 581 *--dp = '\0'; 582 break; 583 } 584 *dp = '\0'; 585 strname = short2str(path); 586 } 587 else 588 strname = short2str(name); 589 return (stat(strname, &stbuf) != -1 && ((S_ISREG(stbuf.st_mode) && 590 /* save time by not calling access() in the hopeless case */ 591 (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && 592 access(strname, X_OK) == 0) || (dir_ok && S_ISDIR(stbuf.st_mode)))); 593 } 594 595 /* The dowhich() is by: 596 * Andreas Luik <luik@isaak.isa.de> 597 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 598 * Azenberstr. 35 599 * D-7000 Stuttgart 1 600 * West-Germany 601 * Thanks!! 602 */ 603 /*ARGSUSED*/ 604 void 605 dowhich(Char **v, struct command *c) 606 { 607 struct wordent lexw[3]; 608 struct varent *vp; 609 610 lexw[0].next = &lexw[1]; 611 lexw[1].next = &lexw[2]; 612 lexw[2].next = &lexw[0]; 613 614 lexw[0].prev = &lexw[2]; 615 lexw[1].prev = &lexw[0]; 616 lexw[2].prev = &lexw[1]; 617 618 lexw[0].word = STRNULL; 619 lexw[2].word = STRret; 620 621 while (*++v) { 622 if ((vp = adrof1(*v, &aliases)) != NULL) { 623 (void)fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); 624 blkpr(cshout, vp->vec); 625 (void)fputc('\n', cshout); 626 set(STRstatus, Strsave(STR0)); 627 } 628 else { 629 lexw[1].word = *v; 630 set(STRstatus, Strsave(tellmewhat(lexw, NULL) ? STR0 : STR1)); 631 } 632 } 633 } 634 635 static int 636 tellmewhat(struct wordent *lexp, Char *str) 637 { 638 struct biltins *bptr; 639 struct wordent *sp; 640 Char *cmd, *s0, *s1, *s2; 641 int i; 642 int aliased, found; 643 Char qc; 644 645 aliased = 0; 646 sp = lexp->next; 647 648 if (adrof1(sp->word, &aliases)) { 649 alias(lexp); 650 sp = lexp->next; 651 aliased = 1; 652 } 653 654 s0 = sp->word; /* to get the memory freeing right... */ 655 656 /* handle quoted alias hack */ 657 if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) 658 (sp->word)++; 659 660 /* do quoting, if it hasn't been done */ 661 s1 = s2 = sp->word; 662 while (*s2) 663 switch (*s2) { 664 case '\'': 665 case '"': 666 qc = *s2++; 667 while (*s2 && *s2 != qc) 668 *s1++ = (Char)(*s2++ | QUOTE); 669 if (*s2) 670 s2++; 671 break; 672 case '\\': 673 if (*++s2) 674 *s1++ = (Char)(*s2++ | QUOTE); 675 break; 676 default: 677 *s1++ = *s2++; 678 } 679 *s1 = '\0'; 680 681 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { 682 if (eq(sp->word, str2short(bptr->bname))) { 683 if (str == NULL) { 684 if (aliased) 685 prlex(cshout, lexp); 686 (void)fprintf(cshout, "%s: shell built-in command.\n", 687 vis_str(sp->word)); 688 } 689 else 690 (void)Strcpy(str, sp->word); 691 sp->word = s0; /* we save and then restore this */ 692 return 1; 693 } 694 } 695 696 sp->word = cmd = globone(sp->word, G_IGNORE); 697 698 if ((i = iscommand(sp->word)) != 0) { 699 Char **pv; 700 struct varent *v; 701 int slash = any(short2str(sp->word), '/'); 702 703 v = adrof(STRpath); 704 if (v == 0 || v->vec[0] == 0 || slash) 705 pv = justabs; 706 else 707 pv = v->vec; 708 709 while (--i) 710 pv++; 711 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { 712 if (!slash) { 713 sp->word = Strspl(STRdotsl, sp->word); 714 prlex(cshout, lexp); 715 xfree((ptr_t) sp->word); 716 } 717 else 718 prlex(cshout, lexp); 719 } 720 else { 721 s1 = Strspl(*pv, STRslash); 722 sp->word = Strspl(s1, sp->word); 723 xfree((ptr_t) s1); 724 if (str == NULL) 725 prlex(cshout, lexp); 726 else 727 (void)Strcpy(str, sp->word); 728 xfree((ptr_t) sp->word); 729 } 730 found = 1; 731 } 732 else { 733 if (str == NULL) { 734 if (aliased) 735 prlex(cshout, lexp); 736 (void)fprintf(csherr, 737 "%s: Command not found.\n", vis_str(sp->word)); 738 } 739 else 740 (void)Strcpy(str, sp->word); 741 found = 0; 742 } 743 sp->word = s0; /* we save and then restore this */ 744 xfree((ptr_t) cmd); 745 return found; 746 } 747