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