exec.c revision 231790
165918Sasmodai/*-
265918Sasmodai * Copyright (c) 1991, 1993
365918Sasmodai *	The Regents of the University of California.  All rights reserved.
465918Sasmodai *
565918Sasmodai * This code is derived from software contributed to Berkeley by
665918Sasmodai * Kenneth Almquist.
765918Sasmodai *
865918Sasmodai * Redistribution and use in source and binary forms, with or without
965918Sasmodai * modification, are permitted provided that the following conditions
1065918Sasmodai * are met:
1165918Sasmodai * 1. Redistributions of source code must retain the above copyright
1265918Sasmodai *    notice, this list of conditions and the following disclaimer.
1365918Sasmodai * 2. Redistributions in binary form must reproduce the above copyright
1465918Sasmodai *    notice, this list of conditions and the following disclaimer in the
1565918Sasmodai *    documentation and/or other materials provided with the distribution.
1665918Sasmodai * 4. Neither the name of the University nor the names of its contributors
1765918Sasmodai *    may be used to endorse or promote products derived from this software
1865918Sasmodai *    without specific prior written permission.
1965918Sasmodai *
2065918Sasmodai * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2165918Sasmodai * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2265918Sasmodai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2365918Sasmodai * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2465918Sasmodai * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2565918Sasmodai * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2665918Sasmodai * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2765918Sasmodai * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2865918Sasmodai * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2965918Sasmodai * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3065918Sasmodai * SUCH DAMAGE.
3165918Sasmodai */
3265918Sasmodai
3365918Sasmodai#ifndef lint
3469356Sasmodai#if 0
3565918Sasmodaistatic char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
3665918Sasmodai#endif
3769356Sasmodai#endif /* not lint */
3869356Sasmodai#include <sys/cdefs.h>
3965918Sasmodai__FBSDID("$FreeBSD: stable/9/bin/sh/exec.c 231790 2012-02-15 22:45:57Z jilles $");
4065918Sasmodai
4165918Sasmodai#include <sys/types.h>
4265918Sasmodai#include <sys/stat.h>
4365918Sasmodai#include <unistd.h>
4465918Sasmodai#include <fcntl.h>
4565918Sasmodai#include <errno.h>
4665918Sasmodai#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 "init.h"
74#include "mystring.h"
75#include "show.h"
76#include "jobs.h"
77#include "alias.h"
78
79
80#define CMDTABLESIZE 31		/* should be prime */
81#define ARB 1			/* actual size determined at run time */
82
83
84
85struct tblentry {
86	struct tblentry *next;	/* next entry in hash chain */
87	union param param;	/* definition of builtin function */
88	int special;		/* flag for special builtin commands */
89	short cmdtype;		/* index identifying command */
90	char rehash;		/* if set, cd done since entry created */
91	char cmdname[ARB];	/* name of command */
92};
93
94
95static struct tblentry *cmdtable[CMDTABLESIZE];
96int exerrno = 0;			/* Last exec error */
97
98
99static void tryexec(char *, char **, char **);
100static void printentry(struct tblentry *, int);
101static struct tblentry *cmdlookup(const char *, int);
102static void delete_cmd_entry(void);
103static void addcmdentry(const char *, struct cmdentry *);
104
105
106
107/*
108 * Exec a program.  Never returns.  If you change this routine, you may
109 * have to change the find_command routine as well.
110 *
111 * The argv array may be changed and element argv[-1] should be writable.
112 */
113
114void
115shellexec(char **argv, char **envp, const char *path, int idx)
116{
117	char *cmdname;
118	int e;
119
120	if (strchr(argv[0], '/') != NULL) {
121		tryexec(argv[0], argv, envp);
122		e = errno;
123	} else {
124		e = ENOENT;
125		while ((cmdname = padvance(&path, argv[0])) != NULL) {
126			if (--idx < 0 && pathopt == NULL) {
127				tryexec(cmdname, argv, envp);
128				if (errno != ENOENT && errno != ENOTDIR)
129					e = errno;
130				if (e == ENOEXEC)
131					break;
132			}
133			stunalloc(cmdname);
134		}
135	}
136
137	/* Map to POSIX errors */
138	if (e == ENOENT || e == ENOTDIR) {
139		exerrno = 127;
140		exerror(EXEXEC, "%s: not found", argv[0]);
141	} else {
142		exerrno = 126;
143		exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
144	}
145}
146
147
148static void
149tryexec(char *cmd, char **argv, char **envp)
150{
151	int e, in;
152	ssize_t n;
153	char buf[256];
154
155	execve(cmd, argv, envp);
156	e = errno;
157	if (e == ENOEXEC) {
158		INTOFF;
159		in = open(cmd, O_RDONLY | O_NONBLOCK);
160		if (in != -1) {
161			n = pread(in, buf, sizeof buf, 0);
162			close(in);
163			if (n > 0 && memchr(buf, '\0', n) != NULL) {
164				errno = ENOEXEC;
165				return;
166			}
167		}
168		*argv = cmd;
169		*--argv = _PATH_BSHELL;
170		execve(_PATH_BSHELL, argv, envp);
171	}
172	errno = e;
173}
174
175/*
176 * Do a path search.  The variable path (passed by reference) should be
177 * set to the start of the path before the first call; padvance will update
178 * this value as it proceeds.  Successive calls to padvance will return
179 * the possible path expansions in sequence.  If an option (indicated by
180 * a percent sign) appears in the path entry then the global variable
181 * pathopt will be set to point to it; otherwise pathopt will be set to
182 * NULL.
183 */
184
185const char *pathopt;
186
187char *
188padvance(const char **path, const char *name)
189{
190	const char *p, *start;
191	char *q;
192	int len;
193
194	if (*path == NULL)
195		return NULL;
196	start = *path;
197	for (p = start; *p && *p != ':' && *p != '%'; p++)
198		; /* nothing */
199	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
200	STARTSTACKSTR(q);
201	CHECKSTRSPACE(len, q);
202	if (p != start) {
203		memcpy(q, start, p - start);
204		q += p - start;
205		*q++ = '/';
206	}
207	strcpy(q, name);
208	pathopt = NULL;
209	if (*p == '%') {
210		pathopt = ++p;
211		while (*p && *p != ':')  p++;
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
225int
226hashcmd(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
235	verbose = 0;
236	while ((c = nextopt("rv")) != '\0') {
237		if (c == 'r') {
238			clearcmdentry();
239		} else if (c == 'v') {
240			verbose++;
241		}
242	}
243	if (*argptr == NULL) {
244		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
245			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
246				if (cmdp->cmdtype == CMDNORMAL)
247					printentry(cmdp, verbose);
248			}
249		}
250		return 0;
251	}
252	while ((name = *argptr) != NULL) {
253		if ((cmdp = cmdlookup(name, 0)) != NULL
254		 && cmdp->cmdtype == CMDNORMAL)
255			delete_cmd_entry();
256		find_command(name, &entry, DO_ERR, pathval());
257		if (verbose) {
258			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
259				cmdp = cmdlookup(name, 0);
260				if (cmdp != NULL)
261					printentry(cmdp, verbose);
262				else
263					outfmt(out2, "%s: not found\n", name);
264			}
265			flushall();
266		}
267		argptr++;
268	}
269	return 0;
270}
271
272
273static void
274printentry(struct tblentry *cmdp, int verbose)
275{
276	int idx;
277	const char *path;
278	char *name;
279
280	if (cmdp->cmdtype == CMDNORMAL) {
281		idx = cmdp->param.index;
282		path = pathval();
283		do {
284			name = padvance(&path, cmdp->cmdname);
285			stunalloc(name);
286		} while (--idx >= 0);
287		out1str(name);
288	} else if (cmdp->cmdtype == CMDBUILTIN) {
289		out1fmt("builtin %s", cmdp->cmdname);
290	} else if (cmdp->cmdtype == CMDFUNCTION) {
291		out1fmt("function %s", cmdp->cmdname);
292		if (verbose) {
293			INTOFF;
294			name = commandtext(getfuncnode(cmdp->param.func));
295			out1c(' ');
296			out1str(name);
297			ckfree(name);
298			INTON;
299		}
300#ifdef DEBUG
301	} else {
302		error("internal error: cmdtype %d", cmdp->cmdtype);
303#endif
304	}
305	if (cmdp->rehash)
306		out1c('*');
307	out1c('\n');
308}
309
310
311
312/*
313 * Resolve a command name.  If you change this routine, you may have to
314 * change the shellexec routine as well.
315 */
316
317void
318find_command(const char *name, struct cmdentry *entry, int act,
319    const char *path)
320{
321	struct tblentry *cmdp, loc_cmd;
322	int idx;
323	int prev;
324	char *fullname;
325	struct stat statb;
326	int e;
327	int i;
328	int spec;
329
330	/* If name contains a slash, don't use the hash table */
331	if (strchr(name, '/') != NULL) {
332		entry->cmdtype = CMDNORMAL;
333		entry->u.index = 0;
334		return;
335	}
336
337	/* If name is in the table, and not invalidated by cd, we're done */
338	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
339		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
340			cmdp = NULL;
341		else
342			goto success;
343	}
344
345	/* Check for builtin next */
346	if ((i = find_builtin(name, &spec)) >= 0) {
347		INTOFF;
348		cmdp = cmdlookup(name, 1);
349		if (cmdp->cmdtype == CMDFUNCTION)
350			cmdp = &loc_cmd;
351		cmdp->cmdtype = CMDBUILTIN;
352		cmdp->param.index = i;
353		cmdp->special = spec;
354		INTON;
355		goto success;
356	}
357
358	/* We have to search path. */
359	prev = -1;		/* where to start */
360	if (cmdp) {		/* doing a rehash */
361		if (cmdp->cmdtype == CMDBUILTIN)
362			prev = -1;
363		else
364			prev = cmdp->param.index;
365	}
366
367	e = ENOENT;
368	idx = -1;
369loop:
370	while ((fullname = padvance(&path, name)) != NULL) {
371		stunalloc(fullname);
372		idx++;
373		if (pathopt) {
374			if (prefix("func", pathopt)) {
375				/* handled below */
376			} else {
377				goto loop;	/* ignore unimplemented options */
378			}
379		}
380		/* if rehash, don't redo absolute path names */
381		if (fullname[0] == '/' && idx <= prev) {
382			if (idx < prev)
383				goto loop;
384			TRACE(("searchexec \"%s\": no change\n", name));
385			goto success;
386		}
387		if (stat(fullname, &statb) < 0) {
388			if (errno != ENOENT && errno != ENOTDIR)
389				e = errno;
390			goto loop;
391		}
392		e = EACCES;	/* if we fail, this will be the error */
393		if (!S_ISREG(statb.st_mode))
394			goto loop;
395		if (pathopt) {		/* this is a %func directory */
396			stalloc(strlen(fullname) + 1);
397			readcmdfile(fullname);
398			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
399				error("%s not defined in %s", name, fullname);
400			stunalloc(fullname);
401			goto success;
402		}
403#ifdef notdef
404		if (statb.st_uid == geteuid()) {
405			if ((statb.st_mode & 0100) == 0)
406				goto loop;
407		} else if (statb.st_gid == getegid()) {
408			if ((statb.st_mode & 010) == 0)
409				goto loop;
410		} else {
411			if ((statb.st_mode & 01) == 0)
412				goto loop;
413		}
414#endif
415		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
416		INTOFF;
417		cmdp = cmdlookup(name, 1);
418		if (cmdp->cmdtype == CMDFUNCTION)
419			cmdp = &loc_cmd;
420		cmdp->cmdtype = CMDNORMAL;
421		cmdp->param.index = idx;
422		INTON;
423		goto success;
424	}
425
426	/* We failed.  If there was an entry for this command, delete it */
427	if (cmdp && cmdp->cmdtype != CMDFUNCTION)
428		delete_cmd_entry();
429	if (act & DO_ERR) {
430		if (e == ENOENT || e == ENOTDIR)
431			outfmt(out2, "%s: not found\n", name);
432		else
433			outfmt(out2, "%s: %s\n", name, strerror(e));
434	}
435	entry->cmdtype = CMDUNKNOWN;
436	entry->u.index = 0;
437	return;
438
439success:
440	cmdp->rehash = 0;
441	entry->cmdtype = cmdp->cmdtype;
442	entry->u = cmdp->param;
443	entry->special = cmdp->special;
444}
445
446
447
448/*
449 * Search the table of builtin commands.
450 */
451
452int
453find_builtin(const char *name, int *special)
454{
455	const struct builtincmd *bp;
456
457	for (bp = builtincmd ; bp->name ; bp++) {
458		if (*bp->name == *name && equal(bp->name, name)) {
459			*special = bp->special;
460			return bp->code;
461		}
462	}
463	return -1;
464}
465
466
467
468/*
469 * Called when a cd is done.  Marks all commands so the next time they
470 * are executed they will be rehashed.
471 */
472
473void
474hashcd(void)
475{
476	struct tblentry **pp;
477	struct tblentry *cmdp;
478
479	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
480		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
481			if (cmdp->cmdtype == CMDNORMAL)
482				cmdp->rehash = 1;
483		}
484	}
485}
486
487
488
489/*
490 * Called before PATH is changed.  The argument is the new value of PATH;
491 * pathval() still returns the old value at this point.  Called with
492 * interrupts off.
493 */
494
495void
496changepath(const char *newval)
497{
498	clearcmdentry();
499}
500
501
502/*
503 * Clear out command entries.  The argument specifies the first entry in
504 * PATH which has changed.
505 */
506
507void
508clearcmdentry(void)
509{
510	struct tblentry **tblp;
511	struct tblentry **pp;
512	struct tblentry *cmdp;
513
514	INTOFF;
515	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
516		pp = tblp;
517		while ((cmdp = *pp) != NULL) {
518			if (cmdp->cmdtype == CMDNORMAL) {
519				*pp = cmdp->next;
520				ckfree(cmdp);
521			} else {
522				pp = &cmdp->next;
523			}
524		}
525	}
526	INTON;
527}
528
529
530/*
531 * Locate a command in the command hash table.  If "add" is nonzero,
532 * add the command to the table if it is not already present.  The
533 * variable "lastcmdentry" is set to point to the address of the link
534 * pointing to the entry, so that delete_cmd_entry can delete the
535 * entry.
536 */
537
538static struct tblentry **lastcmdentry;
539
540
541static struct tblentry *
542cmdlookup(const char *name, int add)
543{
544	int hashval;
545	const char *p;
546	struct tblentry *cmdp;
547	struct tblentry **pp;
548
549	p = name;
550	hashval = *p << 4;
551	while (*p)
552		hashval += *p++;
553	hashval &= 0x7FFF;
554	pp = &cmdtable[hashval % CMDTABLESIZE];
555	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
556		if (equal(cmdp->cmdname, name))
557			break;
558		pp = &cmdp->next;
559	}
560	if (add && cmdp == NULL) {
561		INTOFF;
562		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
563					+ strlen(name) + 1);
564		cmdp->next = NULL;
565		cmdp->cmdtype = CMDUNKNOWN;
566		cmdp->rehash = 0;
567		strcpy(cmdp->cmdname, name);
568		INTON;
569	}
570	lastcmdentry = pp;
571	return cmdp;
572}
573
574/*
575 * Delete the command entry returned on the last lookup.
576 */
577
578static void
579delete_cmd_entry(void)
580{
581	struct tblentry *cmdp;
582
583	INTOFF;
584	cmdp = *lastcmdentry;
585	*lastcmdentry = cmdp->next;
586	ckfree(cmdp);
587	INTON;
588}
589
590
591
592/*
593 * Add a new command entry, replacing any existing command entry for
594 * the same name.
595 */
596
597static void
598addcmdentry(const char *name, struct cmdentry *entry)
599{
600	struct tblentry *cmdp;
601
602	INTOFF;
603	cmdp = cmdlookup(name, 1);
604	if (cmdp->cmdtype == CMDFUNCTION) {
605		unreffunc(cmdp->param.func);
606	}
607	cmdp->cmdtype = entry->cmdtype;
608	cmdp->param = entry->u;
609	INTON;
610}
611
612
613/*
614 * Define a shell function.
615 */
616
617void
618defun(const char *name, union node *func)
619{
620	struct cmdentry entry;
621
622	INTOFF;
623	entry.cmdtype = CMDFUNCTION;
624	entry.u.func = copyfunc(func);
625	addcmdentry(name, &entry);
626	INTON;
627}
628
629
630/*
631 * Delete a function if it exists.
632 */
633
634int
635unsetfunc(const char *name)
636{
637	struct tblentry *cmdp;
638
639	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
640		unreffunc(cmdp->param.func);
641		delete_cmd_entry();
642		return (0);
643	}
644	return (0);
645}
646
647/*
648 * Shared code for the following builtin commands:
649 *    type, command -v, command -V
650 */
651
652int
653typecmd_impl(int argc, char **argv, int cmd, const char *path)
654{
655	struct cmdentry entry;
656	struct tblentry *cmdp;
657	const char *const *pp;
658	struct alias *ap;
659	int i;
660	int error1 = 0;
661
662	if (path != pathval())
663		clearcmdentry();
664
665	for (i = 1; i < argc; i++) {
666		/* First look at the keywords */
667		for (pp = parsekwd; *pp; pp++)
668			if (**pp == *argv[i] && equal(*pp, argv[i]))
669				break;
670
671		if (*pp) {
672			if (cmd == TYPECMD_SMALLV)
673				out1fmt("%s\n", argv[i]);
674			else
675				out1fmt("%s is a shell keyword\n", argv[i]);
676			continue;
677		}
678
679		/* Then look at the aliases */
680		if ((ap = lookupalias(argv[i], 1)) != NULL) {
681			if (cmd == TYPECMD_SMALLV)
682				out1fmt("alias %s='%s'\n", argv[i], ap->val);
683			else
684				out1fmt("%s is an alias for %s\n", argv[i],
685				    ap->val);
686			continue;
687		}
688
689		/* Then check if it is a tracked alias */
690		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
691			entry.cmdtype = cmdp->cmdtype;
692			entry.u = cmdp->param;
693			entry.special = cmdp->special;
694		}
695		else {
696			/* Finally use brute force */
697			find_command(argv[i], &entry, 0, path);
698		}
699
700		switch (entry.cmdtype) {
701		case CMDNORMAL: {
702			if (strchr(argv[i], '/') == NULL) {
703				const char *path2 = path;
704				char *name;
705				int j = entry.u.index;
706				do {
707					name = padvance(&path2, argv[i]);
708					stunalloc(name);
709				} while (--j >= 0);
710				if (cmd == TYPECMD_SMALLV)
711					out1fmt("%s\n", name);
712				else
713					out1fmt("%s is%s %s\n", argv[i],
714					    (cmdp && cmd == TYPECMD_TYPE) ?
715						" a tracked alias for" : "",
716					    name);
717			} else {
718				if (eaccess(argv[i], X_OK) == 0) {
719					if (cmd == TYPECMD_SMALLV)
720						out1fmt("%s\n", argv[i]);
721					else
722						out1fmt("%s is %s\n", argv[i],
723						    argv[i]);
724				} else {
725					if (cmd != TYPECMD_SMALLV)
726						outfmt(out2, "%s: %s\n",
727						    argv[i], strerror(errno));
728					error1 |= 127;
729				}
730			}
731			break;
732		}
733		case CMDFUNCTION:
734			if (cmd == TYPECMD_SMALLV)
735				out1fmt("%s\n", argv[i]);
736			else
737				out1fmt("%s is a shell function\n", argv[i]);
738			break;
739
740		case CMDBUILTIN:
741			if (cmd == TYPECMD_SMALLV)
742				out1fmt("%s\n", argv[i]);
743			else if (entry.special)
744				out1fmt("%s is a special shell builtin\n",
745				    argv[i]);
746			else
747				out1fmt("%s is a shell builtin\n", argv[i]);
748			break;
749
750		default:
751			if (cmd != TYPECMD_SMALLV)
752				outfmt(out2, "%s: not found\n", argv[i]);
753			error1 |= 127;
754			break;
755		}
756	}
757
758	if (path != pathval())
759		clearcmdentry();
760
761	return error1;
762}
763
764/*
765 * Locate and print what a word is...
766 */
767
768int
769typecmd(int argc, char **argv)
770{
771	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
772}
773