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