exec.c revision 231535
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 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/bin/sh/exec.c 231535 2012-02-11 21:06:45Z jilles $");
40
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <unistd.h>
44#include <fcntl.h>
45#include <errno.h>
46#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	int errors;
235
236	errors = 0;
237	verbose = 0;
238	while ((c = nextopt("rv")) != '\0') {
239		if (c == 'r') {
240			clearcmdentry();
241		} else if (c == 'v') {
242			verbose++;
243		}
244	}
245	if (*argptr == NULL) {
246		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
247			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
248				if (cmdp->cmdtype == CMDNORMAL)
249					printentry(cmdp, verbose);
250			}
251		}
252		return 0;
253	}
254	while ((name = *argptr) != NULL) {
255		if ((cmdp = cmdlookup(name, 0)) != NULL
256		 && cmdp->cmdtype == CMDNORMAL)
257			delete_cmd_entry();
258		find_command(name, &entry, DO_ERR, pathval());
259		if (entry.cmdtype == CMDUNKNOWN)
260			errors = 1;
261		else if (verbose) {
262			cmdp = cmdlookup(name, 0);
263			if (cmdp != NULL)
264				printentry(cmdp, verbose);
265			else {
266				outfmt(out2, "%s: not found\n", name);
267				errors = 1;
268			}
269			flushall();
270		}
271		argptr++;
272	}
273	return errors;
274}
275
276
277static void
278printentry(struct tblentry *cmdp, int verbose)
279{
280	int idx;
281	const char *path;
282	char *name;
283
284	if (cmdp->cmdtype == CMDNORMAL) {
285		idx = cmdp->param.index;
286		path = pathval();
287		do {
288			name = padvance(&path, cmdp->cmdname);
289			stunalloc(name);
290		} while (--idx >= 0);
291		out1str(name);
292	} else if (cmdp->cmdtype == CMDBUILTIN) {
293		out1fmt("builtin %s", cmdp->cmdname);
294	} else if (cmdp->cmdtype == CMDFUNCTION) {
295		out1fmt("function %s", cmdp->cmdname);
296		if (verbose) {
297			INTOFF;
298			name = commandtext(getfuncnode(cmdp->param.func));
299			out1c(' ');
300			out1str(name);
301			ckfree(name);
302			INTON;
303		}
304#ifdef DEBUG
305	} else {
306		error("internal error: cmdtype %d", cmdp->cmdtype);
307#endif
308	}
309	if (cmdp->rehash)
310		out1c('*');
311	out1c('\n');
312}
313
314
315
316/*
317 * Resolve a command name.  If you change this routine, you may have to
318 * change the shellexec routine as well.
319 */
320
321void
322find_command(const char *name, struct cmdentry *entry, int act,
323    const char *path)
324{
325	struct tblentry *cmdp, loc_cmd;
326	int idx;
327	int prev;
328	char *fullname;
329	struct stat statb;
330	int e;
331	int i;
332	int spec;
333
334	/* If name contains a slash, don't use the hash table */
335	if (strchr(name, '/') != NULL) {
336		entry->cmdtype = CMDNORMAL;
337		entry->u.index = 0;
338		return;
339	}
340
341	/* If name is in the table, and not invalidated by cd, we're done */
342	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
343		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
344			cmdp = NULL;
345		else
346			goto success;
347	}
348
349	/* Check for builtin next */
350	if ((i = find_builtin(name, &spec)) >= 0) {
351		INTOFF;
352		cmdp = cmdlookup(name, 1);
353		if (cmdp->cmdtype == CMDFUNCTION)
354			cmdp = &loc_cmd;
355		cmdp->cmdtype = CMDBUILTIN;
356		cmdp->param.index = i;
357		cmdp->special = spec;
358		INTON;
359		goto success;
360	}
361
362	/* We have to search path. */
363	prev = -1;		/* where to start */
364	if (cmdp) {		/* doing a rehash */
365		if (cmdp->cmdtype == CMDBUILTIN)
366			prev = -1;
367		else
368			prev = cmdp->param.index;
369	}
370
371	e = ENOENT;
372	idx = -1;
373loop:
374	while ((fullname = padvance(&path, name)) != NULL) {
375		stunalloc(fullname);
376		idx++;
377		if (pathopt) {
378			if (prefix("func", pathopt)) {
379				/* handled below */
380			} else {
381				goto loop;	/* ignore unimplemented options */
382			}
383		}
384		/* if rehash, don't redo absolute path names */
385		if (fullname[0] == '/' && idx <= prev) {
386			if (idx < prev)
387				goto loop;
388			TRACE(("searchexec \"%s\": no change\n", name));
389			goto success;
390		}
391		if (stat(fullname, &statb) < 0) {
392			if (errno != ENOENT && errno != ENOTDIR)
393				e = errno;
394			goto loop;
395		}
396		e = EACCES;	/* if we fail, this will be the error */
397		if (!S_ISREG(statb.st_mode))
398			goto loop;
399		if (pathopt) {		/* this is a %func directory */
400			stalloc(strlen(fullname) + 1);
401			readcmdfile(fullname);
402			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
403				error("%s not defined in %s", name, fullname);
404			stunalloc(fullname);
405			goto success;
406		}
407#ifdef notdef
408		if (statb.st_uid == geteuid()) {
409			if ((statb.st_mode & 0100) == 0)
410				goto loop;
411		} else if (statb.st_gid == getegid()) {
412			if ((statb.st_mode & 010) == 0)
413				goto loop;
414		} else {
415			if ((statb.st_mode & 01) == 0)
416				goto loop;
417		}
418#endif
419		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
420		INTOFF;
421		cmdp = cmdlookup(name, 1);
422		if (cmdp->cmdtype == CMDFUNCTION)
423			cmdp = &loc_cmd;
424		cmdp->cmdtype = CMDNORMAL;
425		cmdp->param.index = idx;
426		INTON;
427		goto success;
428	}
429
430	/* We failed.  If there was an entry for this command, delete it */
431	if (cmdp && cmdp->cmdtype != CMDFUNCTION)
432		delete_cmd_entry();
433	if (act & DO_ERR) {
434		if (e == ENOENT || e == ENOTDIR)
435			outfmt(out2, "%s: not found\n", name);
436		else
437			outfmt(out2, "%s: %s\n", name, strerror(e));
438	}
439	entry->cmdtype = CMDUNKNOWN;
440	entry->u.index = 0;
441	return;
442
443success:
444	cmdp->rehash = 0;
445	entry->cmdtype = cmdp->cmdtype;
446	entry->u = cmdp->param;
447	entry->special = cmdp->special;
448}
449
450
451
452/*
453 * Search the table of builtin commands.
454 */
455
456int
457find_builtin(const char *name, int *special)
458{
459	const struct builtincmd *bp;
460
461	for (bp = builtincmd ; bp->name ; bp++) {
462		if (*bp->name == *name && equal(bp->name, name)) {
463			*special = bp->special;
464			return bp->code;
465		}
466	}
467	return -1;
468}
469
470
471
472/*
473 * Called when a cd is done.  Marks all commands so the next time they
474 * are executed they will be rehashed.
475 */
476
477void
478hashcd(void)
479{
480	struct tblentry **pp;
481	struct tblentry *cmdp;
482
483	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
484		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
485			if (cmdp->cmdtype == CMDNORMAL)
486				cmdp->rehash = 1;
487		}
488	}
489}
490
491
492
493/*
494 * Called before PATH is changed.  The argument is the new value of PATH;
495 * pathval() still returns the old value at this point.  Called with
496 * interrupts off.
497 */
498
499void
500changepath(const char *newval __unused)
501{
502	clearcmdentry();
503}
504
505
506/*
507 * Clear out command entries.  The argument specifies the first entry in
508 * PATH which has changed.
509 */
510
511void
512clearcmdentry(void)
513{
514	struct tblentry **tblp;
515	struct tblentry **pp;
516	struct tblentry *cmdp;
517
518	INTOFF;
519	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
520		pp = tblp;
521		while ((cmdp = *pp) != NULL) {
522			if (cmdp->cmdtype == CMDNORMAL) {
523				*pp = cmdp->next;
524				ckfree(cmdp);
525			} else {
526				pp = &cmdp->next;
527			}
528		}
529	}
530	INTON;
531}
532
533
534/*
535 * Locate a command in the command hash table.  If "add" is nonzero,
536 * add the command to the table if it is not already present.  The
537 * variable "lastcmdentry" is set to point to the address of the link
538 * pointing to the entry, so that delete_cmd_entry can delete the
539 * entry.
540 */
541
542static struct tblentry **lastcmdentry;
543
544
545static struct tblentry *
546cmdlookup(const char *name, int add)
547{
548	int hashval;
549	const char *p;
550	struct tblentry *cmdp;
551	struct tblentry **pp;
552
553	p = name;
554	hashval = *p << 4;
555	while (*p)
556		hashval += *p++;
557	hashval &= 0x7FFF;
558	pp = &cmdtable[hashval % CMDTABLESIZE];
559	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
560		if (equal(cmdp->cmdname, name))
561			break;
562		pp = &cmdp->next;
563	}
564	if (add && cmdp == NULL) {
565		INTOFF;
566		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
567					+ strlen(name) + 1);
568		cmdp->next = NULL;
569		cmdp->cmdtype = CMDUNKNOWN;
570		cmdp->rehash = 0;
571		strcpy(cmdp->cmdname, name);
572		INTON;
573	}
574	lastcmdentry = pp;
575	return cmdp;
576}
577
578/*
579 * Delete the command entry returned on the last lookup.
580 */
581
582static void
583delete_cmd_entry(void)
584{
585	struct tblentry *cmdp;
586
587	INTOFF;
588	cmdp = *lastcmdentry;
589	*lastcmdentry = cmdp->next;
590	ckfree(cmdp);
591	INTON;
592}
593
594
595
596/*
597 * Add a new command entry, replacing any existing command entry for
598 * the same name.
599 */
600
601static void
602addcmdentry(const char *name, struct cmdentry *entry)
603{
604	struct tblentry *cmdp;
605
606	INTOFF;
607	cmdp = cmdlookup(name, 1);
608	if (cmdp->cmdtype == CMDFUNCTION) {
609		unreffunc(cmdp->param.func);
610	}
611	cmdp->cmdtype = entry->cmdtype;
612	cmdp->param = entry->u;
613	INTON;
614}
615
616
617/*
618 * Define a shell function.
619 */
620
621void
622defun(const char *name, union node *func)
623{
624	struct cmdentry entry;
625
626	INTOFF;
627	entry.cmdtype = CMDFUNCTION;
628	entry.u.func = copyfunc(func);
629	addcmdentry(name, &entry);
630	INTON;
631}
632
633
634/*
635 * Delete a function if it exists.
636 */
637
638int
639unsetfunc(const char *name)
640{
641	struct tblentry *cmdp;
642
643	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
644		unreffunc(cmdp->param.func);
645		delete_cmd_entry();
646		return (0);
647	}
648	return (0);
649}
650
651/*
652 * Shared code for the following builtin commands:
653 *    type, command -v, command -V
654 */
655
656int
657typecmd_impl(int argc, char **argv, int cmd, const char *path)
658{
659	struct cmdentry entry;
660	struct tblentry *cmdp;
661	const char *const *pp;
662	struct alias *ap;
663	int i;
664	int error1 = 0;
665
666	if (path != pathval())
667		clearcmdentry();
668
669	for (i = 1; i < argc; i++) {
670		/* First look at the keywords */
671		for (pp = parsekwd; *pp; pp++)
672			if (**pp == *argv[i] && equal(*pp, argv[i]))
673				break;
674
675		if (*pp) {
676			if (cmd == TYPECMD_SMALLV)
677				out1fmt("%s\n", argv[i]);
678			else
679				out1fmt("%s is a shell keyword\n", argv[i]);
680			continue;
681		}
682
683		/* Then look at the aliases */
684		if ((ap = lookupalias(argv[i], 1)) != NULL) {
685			if (cmd == TYPECMD_SMALLV)
686				out1fmt("alias %s='%s'\n", argv[i], ap->val);
687			else
688				out1fmt("%s is an alias for %s\n", argv[i],
689				    ap->val);
690			continue;
691		}
692
693		/* Then check if it is a tracked alias */
694		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
695			entry.cmdtype = cmdp->cmdtype;
696			entry.u = cmdp->param;
697			entry.special = cmdp->special;
698		}
699		else {
700			/* Finally use brute force */
701			find_command(argv[i], &entry, 0, path);
702		}
703
704		switch (entry.cmdtype) {
705		case CMDNORMAL: {
706			if (strchr(argv[i], '/') == NULL) {
707				const char *path2 = path;
708				char *name;
709				int j = entry.u.index;
710				do {
711					name = padvance(&path2, argv[i]);
712					stunalloc(name);
713				} while (--j >= 0);
714				if (cmd == TYPECMD_SMALLV)
715					out1fmt("%s\n", name);
716				else
717					out1fmt("%s is%s %s\n", argv[i],
718					    (cmdp && cmd == TYPECMD_TYPE) ?
719						" a tracked alias for" : "",
720					    name);
721			} else {
722				if (eaccess(argv[i], X_OK) == 0) {
723					if (cmd == TYPECMD_SMALLV)
724						out1fmt("%s\n", argv[i]);
725					else
726						out1fmt("%s is %s\n", argv[i],
727						    argv[i]);
728				} else {
729					if (cmd != TYPECMD_SMALLV)
730						outfmt(out2, "%s: %s\n",
731						    argv[i], strerror(errno));
732					error1 |= 127;
733				}
734			}
735			break;
736		}
737		case CMDFUNCTION:
738			if (cmd == TYPECMD_SMALLV)
739				out1fmt("%s\n", argv[i]);
740			else
741				out1fmt("%s is a shell function\n", argv[i]);
742			break;
743
744		case CMDBUILTIN:
745			if (cmd == TYPECMD_SMALLV)
746				out1fmt("%s\n", argv[i]);
747			else if (entry.special)
748				out1fmt("%s is a special shell builtin\n",
749				    argv[i]);
750			else
751				out1fmt("%s is a shell builtin\n", argv[i]);
752			break;
753
754		default:
755			if (cmd != TYPECMD_SMALLV)
756				outfmt(out2, "%s: not found\n", argv[i]);
757			error1 |= 127;
758			break;
759		}
760	}
761
762	if (path != pathval())
763		clearcmdentry();
764
765	return error1;
766}
767
768/*
769 * Locate and print what a word is...
770 */
771
772int
773typecmd(int argc, char **argv)
774{
775	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
776}
777