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