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