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