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