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