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