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