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