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