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