1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $Id$ 37 */ 38 39 #ifndef lint 40 static char sccsid[] = "@(#)exec.c 8.1 (Berkeley) 5/31/93"; 41 #endif /* not lint */ 42 43 /* 44 * When commands are first encountered, they are entered in a hash table. 45 * This ensures that a full path search will not have to be done for them 46 * on each invocation. 47 * 48 * We should investigate converting to a linear search, even though that 49 * would make the command name "hash" a misnomer. 50 */ 51 52 #include "shell.h" 53 #include "main.h" 54 #include "nodes.h" 55 #include "parser.h" 56 #include "redir.h" 57 #include "eval.h" 58 #include "exec.h" 59 #include "builtins.h" 60 #include "var.h" 61 #include "options.h" 62 #include "input.h" 63 #include "output.h" 64 #include "syntax.h" 65 #include "memalloc.h" 66 #include "error.h" 67 #include "init.h" 68 #include "mystring.h" 69 #include "jobs.h" 70 #include <sys/types.h> 71 #include <sys/stat.h> 72 #include <fcntl.h> 73 #include <errno.h> 74 75 76 #define CMDTABLESIZE 31 /* should be prime */ 77 #define ARB 1 /* actual size determined at run time */ 78 79 80 81 struct tblentry { 82 struct tblentry *next; /* next entry in hash chain */ 83 union param param; /* definition of builtin function */ 84 short cmdtype; /* index identifying command */ 85 char rehash; /* if set, cd done since entry created */ 86 char cmdname[ARB]; /* name of command */ 87 }; 88 89 90 STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 91 STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 92 93 94 #ifdef __STDC__ 95 STATIC void tryexec(char *, char **, char **); 96 STATIC void execinterp(char **, char **); 97 STATIC void printentry(struct tblentry *, int); 98 STATIC void clearcmdentry(int); 99 STATIC struct tblentry *cmdlookup(char *, int); 100 STATIC void delete_cmd_entry(void); 101 #else 102 STATIC void tryexec(); 103 STATIC void execinterp(); 104 STATIC void printentry(); 105 STATIC void clearcmdentry(); 106 STATIC struct tblentry *cmdlookup(); 107 STATIC void delete_cmd_entry(); 108 #endif 109 110 111 112 /* 113 * Exec a program. Never returns. If you change this routine, you may 114 * have to change the find_command routine as well. 115 */ 116 117 void 118 shellexec(argv, envp, path, index) 119 char **argv, **envp; 120 char *path; 121 { 122 char *cmdname; 123 int e; 124 125 if (strchr(argv[0], '/') != NULL) { 126 tryexec(argv[0], argv, envp); 127 e = errno; 128 } else { 129 e = ENOENT; 130 while ((cmdname = padvance(&path, argv[0])) != NULL) { 131 if (--index < 0 && pathopt == NULL) { 132 tryexec(cmdname, argv, envp); 133 if (errno != ENOENT && errno != ENOTDIR) 134 e = errno; 135 } 136 stunalloc(cmdname); 137 } 138 } 139 error2(argv[0], errmsg(e, E_EXEC)); 140 } 141 142 143 STATIC void 144 tryexec(cmd, argv, envp) 145 char *cmd; 146 char **argv; 147 char **envp; 148 { 149 int e; 150 char *p; 151 152 #ifdef SYSV 153 do { 154 execve(cmd, argv, envp); 155 } while (errno == EINTR); 156 #else 157 execve(cmd, argv, envp); 158 #endif 159 e = errno; 160 if (e == ENOEXEC) { 161 initshellproc(); 162 setinputfile(cmd, 0); 163 commandname = arg0 = savestr(argv[0]); 164 #ifndef BSD 165 pgetc(); pungetc(); /* fill up input buffer */ 166 p = parsenextc; 167 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 168 argv[0] = cmd; 169 execinterp(argv, envp); 170 } 171 #endif 172 setparam(argv + 1); 173 exraise(EXSHELLPROC); 174 /*NOTREACHED*/ 175 } 176 errno = e; 177 } 178 179 180 #ifndef BSD 181 /* 182 * Execute an interpreter introduced by "#!", for systems where this 183 * feature has not been built into the kernel. If the interpreter is 184 * the shell, return (effectively ignoring the "#!"). If the execution 185 * of the interpreter fails, exit. 186 * 187 * This code peeks inside the input buffer in order to avoid actually 188 * reading any input. It would benefit from a rewrite. 189 */ 190 191 #define NEWARGS 5 192 193 STATIC void 194 execinterp(argv, envp) 195 char **argv, **envp; 196 { 197 int n; 198 char *inp; 199 char *outp; 200 char c; 201 char *p; 202 char **ap; 203 char *newargs[NEWARGS]; 204 int i; 205 char **ap2; 206 char **new; 207 208 n = parsenleft - 2; 209 inp = parsenextc + 2; 210 ap = newargs; 211 for (;;) { 212 while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 213 inp++; 214 if (n < 0) 215 goto bad; 216 if ((c = *inp++) == '\n') 217 break; 218 if (ap == &newargs[NEWARGS]) 219 bad: error("Bad #! line"); 220 STARTSTACKSTR(outp); 221 do { 222 STPUTC(c, outp); 223 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 224 STPUTC('\0', outp); 225 n++, inp--; 226 *ap++ = grabstackstr(outp); 227 } 228 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 229 p = newargs[0]; 230 for (;;) { 231 if (equal(p, "sh") || equal(p, "ash")) { 232 return; 233 } 234 while (*p != '/') { 235 if (*p == '\0') 236 goto break2; 237 p++; 238 } 239 p++; 240 } 241 break2:; 242 } 243 i = (char *)ap - (char *)newargs; /* size in bytes */ 244 if (i == 0) 245 error("Bad #! line"); 246 for (ap2 = argv ; *ap2++ != NULL ; ); 247 new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 248 ap = newargs, ap2 = new; 249 while ((i -= sizeof (char **)) >= 0) 250 *ap2++ = *ap++; 251 ap = argv; 252 while (*ap2++ = *ap++); 253 shellexec(new, envp, pathval(), 0); 254 } 255 #endif 256 257 258 259 /* 260 * Do a path search. The variable path (passed by reference) should be 261 * set to the start of the path before the first call; padvance will update 262 * this value as it proceeds. Successive calls to padvance will return 263 * the possible path expansions in sequence. If an option (indicated by 264 * a percent sign) appears in the path entry then the global variable 265 * pathopt will be set to point to it; otherwise pathopt will be set to 266 * NULL. 267 */ 268 269 char *pathopt; 270 271 char * 272 padvance(path, name) 273 char **path; 274 char *name; 275 { 276 register char *p, *q; 277 char *start; 278 int len; 279 280 if (*path == NULL) 281 return NULL; 282 start = *path; 283 for (p = start ; *p && *p != ':' && *p != '%' ; p++); 284 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 285 while (stackblocksize() < len) 286 growstackblock(); 287 q = stackblock(); 288 if (p != start) { 289 bcopy(start, q, p - start); 290 q += p - start; 291 *q++ = '/'; 292 } 293 strcpy(q, name); 294 pathopt = NULL; 295 if (*p == '%') { 296 pathopt = ++p; 297 while (*p && *p != ':') p++; 298 } 299 if (*p == ':') 300 *path = p + 1; 301 else 302 *path = NULL; 303 return stalloc(len); 304 } 305 306 307 308 /*** Command hashing code ***/ 309 310 311 hashcmd(argc, argv) char **argv; { 312 struct tblentry **pp; 313 struct tblentry *cmdp; 314 int c; 315 int verbose; 316 struct cmdentry entry; 317 char *name; 318 319 verbose = 0; 320 while ((c = nextopt("rv")) != '\0') { 321 if (c == 'r') { 322 clearcmdentry(0); 323 } else if (c == 'v') { 324 verbose++; 325 } 326 } 327 if (*argptr == NULL) { 328 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 329 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 330 printentry(cmdp, verbose); 331 } 332 } 333 return 0; 334 } 335 while ((name = *argptr) != NULL) { 336 if ((cmdp = cmdlookup(name, 0)) != NULL 337 && (cmdp->cmdtype == CMDNORMAL 338 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 339 delete_cmd_entry(); 340 find_command(name, &entry, 1); 341 if (verbose) { 342 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 343 cmdp = cmdlookup(name, 0); 344 printentry(cmdp, verbose); 345 } 346 flushall(); 347 } 348 argptr++; 349 } 350 return 0; 351 } 352 353 354 STATIC void 355 printentry(cmdp, verbose) 356 struct tblentry *cmdp; 357 int verbose; 358 { 359 int index; 360 char *path; 361 char *name; 362 363 if (cmdp->cmdtype == CMDNORMAL) { 364 index = cmdp->param.index; 365 path = pathval(); 366 do { 367 name = padvance(&path, cmdp->cmdname); 368 stunalloc(name); 369 } while (--index >= 0); 370 out1str(name); 371 } else if (cmdp->cmdtype == CMDBUILTIN) { 372 out1fmt("builtin %s", cmdp->cmdname); 373 } else if (cmdp->cmdtype == CMDFUNCTION) { 374 out1fmt("function %s", cmdp->cmdname); 375 if (verbose) { 376 INTOFF; 377 name = commandtext(cmdp->param.func); 378 out1c(' '); 379 out1str(name); 380 ckfree(name); 381 INTON; 382 } 383 #ifdef DEBUG 384 } else { 385 error("internal error: cmdtype %d", cmdp->cmdtype); 386 #endif 387 } 388 if (cmdp->rehash) 389 out1c('*'); 390 out1c('\n'); 391 } 392 393 394 395 /* 396 * Resolve a command name. If you change this routine, you may have to 397 * change the shellexec routine as well. 398 */ 399 400 void 401 find_command(name, entry, printerr) 402 char *name; 403 struct cmdentry *entry; 404 { 405 struct tblentry *cmdp; 406 int index; 407 int prev; 408 char *path; 409 char *fullname; 410 struct stat statb; 411 int e; 412 int i; 413 414 /* If name contains a slash, don't use the hash table */ 415 if (strchr(name, '/') != NULL) { 416 entry->cmdtype = CMDNORMAL; 417 entry->u.index = 0; 418 return; 419 } 420 421 /* If name is in the table, and not invalidated by cd, we're done */ 422 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) 423 goto success; 424 425 /* If %builtin not in path, check for builtin next */ 426 if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { 427 INTOFF; 428 cmdp = cmdlookup(name, 1); 429 cmdp->cmdtype = CMDBUILTIN; 430 cmdp->param.index = i; 431 INTON; 432 goto success; 433 } 434 435 /* We have to search path. */ 436 prev = -1; /* where to start */ 437 if (cmdp) { /* doing a rehash */ 438 if (cmdp->cmdtype == CMDBUILTIN) 439 prev = builtinloc; 440 else 441 prev = cmdp->param.index; 442 } 443 444 path = pathval(); 445 e = ENOENT; 446 index = -1; 447 loop: 448 while ((fullname = padvance(&path, name)) != NULL) { 449 stunalloc(fullname); 450 index++; 451 if (pathopt) { 452 if (prefix("builtin", pathopt)) { 453 if ((i = find_builtin(name)) < 0) 454 goto loop; 455 INTOFF; 456 cmdp = cmdlookup(name, 1); 457 cmdp->cmdtype = CMDBUILTIN; 458 cmdp->param.index = i; 459 INTON; 460 goto success; 461 } else if (prefix("func", pathopt)) { 462 /* handled below */ 463 } else { 464 goto loop; /* ignore unimplemented options */ 465 } 466 } 467 /* if rehash, don't redo absolute path names */ 468 if (fullname[0] == '/' && index <= prev) { 469 if (index < prev) 470 goto loop; 471 TRACE(("searchexec \"%s\": no change\n", name)); 472 goto success; 473 } 474 while (stat(fullname, &statb) < 0) { 475 #ifdef SYSV 476 if (errno == EINTR) 477 continue; 478 #endif 479 if (errno != ENOENT && errno != ENOTDIR) 480 e = errno; 481 goto loop; 482 } 483 e = EACCES; /* if we fail, this will be the error */ 484 if ((statb.st_mode & S_IFMT) != S_IFREG) 485 goto loop; 486 if (pathopt) { /* this is a %func directory */ 487 stalloc(strlen(fullname) + 1); 488 readcmdfile(fullname); 489 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) 490 error("%s not defined in %s", name, fullname); 491 stunalloc(fullname); 492 goto success; 493 } 494 #ifdef notdef 495 if (statb.st_uid == geteuid()) { 496 if ((statb.st_mode & 0100) == 0) 497 goto loop; 498 } else if (statb.st_gid == getegid()) { 499 if ((statb.st_mode & 010) == 0) 500 goto loop; 501 } else { 502 if ((statb.st_mode & 01) == 0) 503 goto loop; 504 } 505 #endif 506 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); 507 INTOFF; 508 cmdp = cmdlookup(name, 1); 509 cmdp->cmdtype = CMDNORMAL; 510 cmdp->param.index = index; 511 INTON; 512 goto success; 513 } 514 515 /* We failed. If there was an entry for this command, delete it */ 516 if (cmdp) 517 delete_cmd_entry(); 518 if (printerr) 519 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 520 entry->cmdtype = CMDUNKNOWN; 521 return; 522 523 success: 524 cmdp->rehash = 0; 525 entry->cmdtype = cmdp->cmdtype; 526 entry->u = cmdp->param; 527 } 528 529 530 531 /* 532 * Search the table of builtin commands. 533 */ 534 535 int 536 find_builtin(name) 537 char *name; 538 { 539 const register struct builtincmd *bp; 540 541 for (bp = builtincmd ; bp->name ; bp++) { 542 if (*bp->name == *name && equal(bp->name, name)) 543 return bp->code; 544 } 545 return -1; 546 } 547 548 549 550 /* 551 * Called when a cd is done. Marks all commands so the next time they 552 * are executed they will be rehashed. 553 */ 554 555 void 556 hashcd() { 557 struct tblentry **pp; 558 struct tblentry *cmdp; 559 560 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 561 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 562 if (cmdp->cmdtype == CMDNORMAL 563 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) 564 cmdp->rehash = 1; 565 } 566 } 567 } 568 569 570 571 /* 572 * Called before PATH is changed. The argument is the new value of PATH; 573 * pathval() still returns the old value at this point. Called with 574 * interrupts off. 575 */ 576 577 void 578 changepath(newval) 579 char *newval; 580 { 581 char *old, *new; 582 int index; 583 int firstchange; 584 int bltin; 585 586 old = pathval(); 587 new = newval; 588 firstchange = 9999; /* assume no change */ 589 index = 0; 590 bltin = -1; 591 for (;;) { 592 if (*old != *new) { 593 firstchange = index; 594 if (*old == '\0' && *new == ':' 595 || *old == ':' && *new == '\0') 596 firstchange++; 597 old = new; /* ignore subsequent differences */ 598 } 599 if (*new == '\0') 600 break; 601 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 602 bltin = index; 603 if (*new == ':') { 604 index++; 605 } 606 new++, old++; 607 } 608 if (builtinloc < 0 && bltin >= 0) 609 builtinloc = bltin; /* zap builtins */ 610 if (builtinloc >= 0 && bltin < 0) 611 firstchange = 0; 612 clearcmdentry(firstchange); 613 builtinloc = bltin; 614 } 615 616 617 /* 618 * Clear out command entries. The argument specifies the first entry in 619 * PATH which has changed. 620 */ 621 622 STATIC void 623 clearcmdentry(firstchange) { 624 struct tblentry **tblp; 625 struct tblentry **pp; 626 struct tblentry *cmdp; 627 628 INTOFF; 629 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 630 pp = tblp; 631 while ((cmdp = *pp) != NULL) { 632 if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange 633 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { 634 *pp = cmdp->next; 635 ckfree(cmdp); 636 } else { 637 pp = &cmdp->next; 638 } 639 } 640 } 641 INTON; 642 } 643 644 645 /* 646 * Delete all functions. 647 */ 648 649 #ifdef mkinit 650 MKINIT void deletefuncs(); 651 652 SHELLPROC { 653 deletefuncs(); 654 } 655 #endif 656 657 void 658 deletefuncs() { 659 struct tblentry **tblp; 660 struct tblentry **pp; 661 struct tblentry *cmdp; 662 663 INTOFF; 664 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 665 pp = tblp; 666 while ((cmdp = *pp) != NULL) { 667 if (cmdp->cmdtype == CMDFUNCTION) { 668 *pp = cmdp->next; 669 freefunc(cmdp->param.func); 670 ckfree(cmdp); 671 } else { 672 pp = &cmdp->next; 673 } 674 } 675 } 676 INTON; 677 } 678 679 680 681 /* 682 * Locate a command in the command hash table. If "add" is nonzero, 683 * add the command to the table if it is not already present. The 684 * variable "lastcmdentry" is set to point to the address of the link 685 * pointing to the entry, so that delete_cmd_entry can delete the 686 * entry. 687 */ 688 689 struct tblentry **lastcmdentry; 690 691 692 STATIC struct tblentry * 693 cmdlookup(name, add) 694 char *name; 695 { 696 int hashval; 697 register char *p; 698 struct tblentry *cmdp; 699 struct tblentry **pp; 700 701 p = name; 702 hashval = *p << 4; 703 while (*p) 704 hashval += *p++; 705 hashval &= 0x7FFF; 706 pp = &cmdtable[hashval % CMDTABLESIZE]; 707 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 708 if (equal(cmdp->cmdname, name)) 709 break; 710 pp = &cmdp->next; 711 } 712 if (add && cmdp == NULL) { 713 INTOFF; 714 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 715 + strlen(name) + 1); 716 cmdp->next = NULL; 717 cmdp->cmdtype = CMDUNKNOWN; 718 cmdp->rehash = 0; 719 strcpy(cmdp->cmdname, name); 720 INTON; 721 } 722 lastcmdentry = pp; 723 return cmdp; 724 } 725 726 /* 727 * Delete the command entry returned on the last lookup. 728 */ 729 730 STATIC void 731 delete_cmd_entry() { 732 struct tblentry *cmdp; 733 734 INTOFF; 735 cmdp = *lastcmdentry; 736 *lastcmdentry = cmdp->next; 737 ckfree(cmdp); 738 INTON; 739 } 740 741 742 743 #ifdef notdef 744 void 745 getcmdentry(name, entry) 746 char *name; 747 struct cmdentry *entry; 748 { 749 struct tblentry *cmdp = cmdlookup(name, 0); 750 751 if (cmdp) { 752 entry->u = cmdp->param; 753 entry->cmdtype = cmdp->cmdtype; 754 } else { 755 entry->cmdtype = CMDUNKNOWN; 756 entry->u.index = 0; 757 } 758 } 759 #endif 760 761 762 /* 763 * Add a new command entry, replacing any existing command entry for 764 * the same name. 765 */ 766 767 void 768 addcmdentry(name, entry) 769 char *name; 770 struct cmdentry *entry; 771 { 772 struct tblentry *cmdp; 773 774 INTOFF; 775 cmdp = cmdlookup(name, 1); 776 if (cmdp->cmdtype == CMDFUNCTION) { 777 freefunc(cmdp->param.func); 778 } 779 cmdp->cmdtype = entry->cmdtype; 780 cmdp->param = entry->u; 781 INTON; 782 } 783 784 785 /* 786 * Define a shell function. 787 */ 788 789 void 790 defun(name, func) 791 char *name; 792 union node *func; 793 { 794 struct cmdentry entry; 795 796 INTOFF; 797 entry.cmdtype = CMDFUNCTION; 798 entry.u.func = copyfunc(func); 799 addcmdentry(name, &entry); 800 INTON; 801 } 802 803 804 /* 805 * Delete a function if it exists. 806 */ 807 808 int 809 unsetfunc(name) 810 char *name; 811 { 812 struct tblentry *cmdp; 813 814 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 815 freefunc(cmdp->param.func); 816 delete_cmd_entry(); 817 return (0); 818 } 819 return (1); 820 } 821