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