exec.c revision 2111
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 38static 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 <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 79struct 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 88STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 89STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 90 91 92#ifdef __STDC__ 93STATIC void tryexec(char *, char **, char **); 94STATIC void execinterp(char **, char **); 95STATIC void printentry(struct tblentry *, int); 96STATIC void clearcmdentry(int); 97STATIC struct tblentry *cmdlookup(char *, int); 98STATIC void delete_cmd_entry(void); 99#else 100STATIC void tryexec(); 101STATIC void execinterp(); 102STATIC void printentry(); 103STATIC void clearcmdentry(); 104STATIC struct tblentry *cmdlookup(); 105STATIC 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 115void 116shellexec(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 141STATIC void 142tryexec(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 191STATIC void 192execinterp(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]) 217bad: 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 } 239break2:; 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 267char *pathopt; 268 269char * 270padvance(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 309hashcmd(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 352STATIC void 353printentry(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 398void 399find_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; 445loop: 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 521success: 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 533int 534find_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 553void 554hashcd() { 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 575void 576changepath(newval) 577 char *newval; 578 { 579 char *old, *new; 580 int index; 581 int firstchange; 582 int bltin; 583 584 old = pathval(); 585 new = newval; 586 firstchange = 9999; /* assume no change */ 587 index = 0; 588 bltin = -1; 589 for (;;) { 590 if (*old != *new) { 591 firstchange = index; 592 if (*old == '\0' && *new == ':' 593 || *old == ':' && *new == '\0') 594 firstchange++; 595 old = new; /* ignore subsequent differences */ 596 } 597 if (*new == '\0') 598 break; 599 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 600 bltin = index; 601 if (*new == ':') { 602 index++; 603 } 604 new++, old++; 605 } 606 if (builtinloc < 0 && bltin >= 0) 607 builtinloc = bltin; /* zap builtins */ 608 if (builtinloc >= 0 && bltin < 0) 609 firstchange = 0; 610 clearcmdentry(firstchange); 611 builtinloc = bltin; 612} 613 614 615/* 616 * Clear out command entries. The argument specifies the first entry in 617 * PATH which has changed. 618 */ 619 620STATIC void 621clearcmdentry(firstchange) { 622 struct tblentry **tblp; 623 struct tblentry **pp; 624 struct tblentry *cmdp; 625 626 INTOFF; 627 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 628 pp = tblp; 629 while ((cmdp = *pp) != NULL) { 630 if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange 631 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { 632 *pp = cmdp->next; 633 ckfree(cmdp); 634 } else { 635 pp = &cmdp->next; 636 } 637 } 638 } 639 INTON; 640} 641 642 643/* 644 * Delete all functions. 645 */ 646 647#ifdef mkinit 648MKINIT void deletefuncs(); 649 650SHELLPROC { 651 deletefuncs(); 652} 653#endif 654 655void 656deletefuncs() { 657 struct tblentry **tblp; 658 struct tblentry **pp; 659 struct tblentry *cmdp; 660 661 INTOFF; 662 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 663 pp = tblp; 664 while ((cmdp = *pp) != NULL) { 665 if (cmdp->cmdtype == CMDFUNCTION) { 666 *pp = cmdp->next; 667 freefunc(cmdp->param.func); 668 ckfree(cmdp); 669 } else { 670 pp = &cmdp->next; 671 } 672 } 673 } 674 INTON; 675} 676 677 678 679/* 680 * Locate a command in the command hash table. If "add" is nonzero, 681 * add the command to the table if it is not already present. The 682 * variable "lastcmdentry" is set to point to the address of the link 683 * pointing to the entry, so that delete_cmd_entry can delete the 684 * entry. 685 */ 686 687struct tblentry **lastcmdentry; 688 689 690STATIC struct tblentry * 691cmdlookup(name, add) 692 char *name; 693 { 694 int hashval; 695 register char *p; 696 struct tblentry *cmdp; 697 struct tblentry **pp; 698 699 p = name; 700 hashval = *p << 4; 701 while (*p) 702 hashval += *p++; 703 hashval &= 0x7FFF; 704 pp = &cmdtable[hashval % CMDTABLESIZE]; 705 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 706 if (equal(cmdp->cmdname, name)) 707 break; 708 pp = &cmdp->next; 709 } 710 if (add && cmdp == NULL) { 711 INTOFF; 712 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 713 + strlen(name) + 1); 714 cmdp->next = NULL; 715 cmdp->cmdtype = CMDUNKNOWN; 716 cmdp->rehash = 0; 717 strcpy(cmdp->cmdname, name); 718 INTON; 719 } 720 lastcmdentry = pp; 721 return cmdp; 722} 723 724/* 725 * Delete the command entry returned on the last lookup. 726 */ 727 728STATIC void 729delete_cmd_entry() { 730 struct tblentry *cmdp; 731 732 INTOFF; 733 cmdp = *lastcmdentry; 734 *lastcmdentry = cmdp->next; 735 ckfree(cmdp); 736 INTON; 737} 738 739 740 741#ifdef notdef 742void 743getcmdentry(name, entry) 744 char *name; 745 struct cmdentry *entry; 746 { 747 struct tblentry *cmdp = cmdlookup(name, 0); 748 749 if (cmdp) { 750 entry->u = cmdp->param; 751 entry->cmdtype = cmdp->cmdtype; 752 } else { 753 entry->cmdtype = CMDUNKNOWN; 754 entry->u.index = 0; 755 } 756} 757#endif 758 759 760/* 761 * Add a new command entry, replacing any existing command entry for 762 * the same name. 763 */ 764 765void 766addcmdentry(name, entry) 767 char *name; 768 struct cmdentry *entry; 769 { 770 struct tblentry *cmdp; 771 772 INTOFF; 773 cmdp = cmdlookup(name, 1); 774 if (cmdp->cmdtype == CMDFUNCTION) { 775 freefunc(cmdp->param.func); 776 } 777 cmdp->cmdtype = entry->cmdtype; 778 cmdp->param = entry->u; 779 INTON; 780} 781 782 783/* 784 * Define a shell function. 785 */ 786 787void 788defun(name, func) 789 char *name; 790 union node *func; 791 { 792 struct cmdentry entry; 793 794 INTOFF; 795 entry.cmdtype = CMDFUNCTION; 796 entry.u.func = copyfunc(func); 797 addcmdentry(name, &entry); 798 INTON; 799} 800 801 802/* 803 * Delete a function if it exists. 804 */ 805 806int 807unsetfunc(name) 808 char *name; 809 { 810 struct tblentry *cmdp; 811 812 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { 813 freefunc(cmdp->param.func); 814 delete_cmd_entry(); 815 return (0); 816 } 817 return (1); 818} 819