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