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