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