xref: /original-bsd/bin/csh/exec.c (revision 29faa970)
1 /*-
2  * Copyright (c) 1980, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)exec.c	8.1 (Berkeley) 05/31/93";
10 #endif /* not lint */
11 
12 #include <sys/types.h>
13 #include <sys/param.h>
14 #include <dirent.h>
15 #include <fcntl.h>
16 #include <sys/stat.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #if __STDC__
22 # include <stdarg.h>
23 #else
24 # include <varargs.h>
25 #endif
26 
27 #include "csh.h"
28 #include "extern.h"
29 
30 /*
31  * System level search and execute of a command.  We look in each directory
32  * for the specified command name.  If the name contains a '/' then we
33  * execute only the full path name.  If there is no search path then we
34  * execute only full path names.
35  */
36 extern char **environ;
37 
38 /*
39  * As we search for the command we note the first non-trivial error
40  * message for presentation to the user.  This allows us often
41  * to show that a file has the wrong mode/no access when the file
42  * is not in the last component of the search path, so we must
43  * go on after first detecting the error.
44  */
45 static char *exerr;		/* Execution error message */
46 static Char *expath;		/* Path for exerr */
47 
48 /*
49  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
50  * to hash execs.  If it is allocated (havhash true), then to tell
51  * whether ``name'' is (possibly) present in the i'th component
52  * of the variable path, you look at the bit in xhash indexed by
53  * hash(hashname("name"), i).  This is setup automatically
54  * after .login is executed, and recomputed whenever ``path'' is
55  * changed.
56  * The two part hash function is designed to let texec() call the
57  * more expensive hashname() only once and the simple hash() several
58  * times (once for each path component checked).
59  * Byte size is assumed to be 8.
60  */
61 #define	HSHSIZ		8192	/* 1k bytes */
62 #define HSHMASK		(HSHSIZ - 1)
63 #define HSHMUL		243
64 static char xhash[HSHSIZ / 8];
65 
66 #define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
67 #define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
68 #define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
69 static int hits, misses;
70 
71 /* Dummy search path for just absolute search when no path */
72 static Char *justabs[] = {STRNULL, 0};
73 
74 static void	pexerr __P((void));
75 static void	texec __P((Char *, Char **));
76 static int	hashname __P((Char *));
77 static void 	tellmewhat __P((struct wordent *));
78 static int	executable __P((Char *, Char *, bool));
79 static int	iscommand __P((Char *));
80 
81 
82 void
83 /*ARGSUSED*/
84 doexec(v, t)
85     Char **v;
86     struct command *t;
87 {
88     register Char *dp, **pv, **av, *sav;
89     register struct varent *pathv;
90     register bool slash;
91     register int hashval = 0, hashval1, i;
92     Char   *blk[2];
93 
94     /*
95      * Glob the command name. We will search $path even if this does something,
96      * as in sh but not in csh.  One special case: if there is no PATH, then we
97      * execute only commands which start with '/'.
98      */
99     blk[0] = t->t_dcom[0];
100     blk[1] = 0;
101     gflag = 0, tglob(blk);
102     if (gflag) {
103 	pv = globall(blk);
104 	if (pv == 0) {
105 	    setname(vis_str(blk[0]));
106 	    stderror(ERR_NAME | ERR_NOMATCH);
107 	}
108 	gargv = 0;
109     }
110     else
111 	pv = saveblk(blk);
112 
113     trim(pv);
114 
115     exerr = 0;
116     expath = Strsave(pv[0]);
117     Vexpath = expath;
118 
119     pathv = adrof(STRpath);
120     if (pathv == 0 && expath[0] != '/') {
121 	blkfree(pv);
122 	pexerr();
123     }
124     slash = any(short2str(expath), '/');
125 
126     /*
127      * Glob the argument list, if necessary. Otherwise trim off the quote bits.
128      */
129     gflag = 0;
130     av = &t->t_dcom[1];
131     tglob(av);
132     if (gflag) {
133 	av = globall(av);
134 	if (av == 0) {
135 	    blkfree(pv);
136 	    setname(vis_str(expath));
137 	    stderror(ERR_NAME | ERR_NOMATCH);
138 	}
139 	gargv = 0;
140     }
141     else
142 	av = saveblk(av);
143 
144     blkfree(t->t_dcom);
145     t->t_dcom = blkspl(pv, av);
146     xfree((ptr_t) pv);
147     xfree((ptr_t) av);
148     av = t->t_dcom;
149     trim(av);
150 
151     if (*av == NULL || **av == '\0')
152 	pexerr();
153 
154     xechoit(av);		/* Echo command if -x */
155     /*
156      * Since all internal file descriptors are set to close on exec, we don't
157      * need to close them explicitly here.  Just reorient ourselves for error
158      * messages.
159      */
160     SHIN = 0;
161     SHOUT = 1;
162     SHERR = 2;
163     OLDSTD = 0;
164     /*
165      * We must do this AFTER any possible forking (like `foo` in glob) so that
166      * this shell can still do subprocesses.
167      */
168     (void) sigsetmask((sigset_t) 0);
169     /*
170      * If no path, no words in path, or a / in the filename then restrict the
171      * command search.
172      */
173     if (pathv == 0 || pathv->vec[0] == 0 || slash)
174 	pv = justabs;
175     else
176 	pv = pathv->vec;
177     sav = Strspl(STRslash, *av);/* / command name for postpending */
178     Vsav = sav;
179     if (havhash)
180 	hashval = hashname(*av);
181     i = 0;
182     hits++;
183     do {
184 	/*
185 	 * Try to save time by looking at the hash table for where this command
186 	 * could be.  If we are doing delayed hashing, then we put the names in
187 	 * one at a time, as the user enters them.  This is kinda like Korn
188 	 * Shell's "tracked aliases".
189 	 */
190 	if (!slash && pv[0][0] == '/' && havhash) {
191 	    hashval1 = hash(hashval, i);
192 	    if (!bit(xhash, hashval1))
193 		goto cont;
194 	}
195 	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
196 	    texec(*av, av);
197 	else {
198 	    dp = Strspl(*pv, sav);
199 	    Vdp = dp;
200 	    texec(dp, av);
201 	    Vdp = 0;
202 	    xfree((ptr_t) dp);
203 	}
204 	misses++;
205 cont:
206 	pv++;
207 	i++;
208     } while (*pv);
209     hits--;
210     Vsav = 0;
211     xfree((ptr_t) sav);
212     pexerr();
213 }
214 
215 static void
216 pexerr()
217 {
218     /* Couldn't find the damn thing */
219     if (expath) {
220 	setname(vis_str(expath));
221 	Vexpath = 0;
222 	xfree((ptr_t) expath);
223 	expath = 0;
224     }
225     else
226 	setname("");
227     if (exerr)
228 	stderror(ERR_NAME | ERR_STRING, exerr);
229     stderror(ERR_NAME | ERR_COMMAND);
230 }
231 
232 /*
233  * Execute command f, arg list t.
234  * Record error message if not found.
235  * Also do shell scripts here.
236  */
237 static void
238 texec(sf, st)
239     Char   *sf;
240     register Char **st;
241 {
242     register char **t;
243     register char *f;
244     register struct varent *v;
245     register Char **vp;
246     Char   *lastsh[2];
247     int     fd;
248     unsigned char c;
249     Char   *st0, **ost;
250 
251     /* The order for the conversions is significant */
252     t = short2blk(st);
253     f = short2str(sf);
254     Vt = t;
255     errno = 0;			/* don't use a previous error */
256     (void) execve(f, t, environ);
257     Vt = 0;
258     blkfree((Char **) t);
259     switch (errno) {
260 
261     case ENOEXEC:
262 	/*
263 	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
264 	 * it, don't feed it to the shell if it looks like a binary!
265 	 */
266 	if ((fd = open(f, O_RDONLY)) != -1) {
267 	    if (read(fd, (char *) &c, 1) == 1) {
268 		if (!Isprint(c) && (c != '\n' && c != '\t')) {
269 		    (void) close(fd);
270 		    /*
271 		     * We *know* what ENOEXEC means.
272 		     */
273 		    stderror(ERR_ARCH, f, strerror(errno));
274 		}
275 	    }
276 #ifdef _PATH_BSHELL
277 	    else
278 		c = '#';
279 #endif
280 	    (void) close(fd);
281 	}
282 	/*
283 	 * If there is an alias for shell, then put the words of the alias in
284 	 * front of the argument list replacing the command name. Note no
285 	 * interpretation of the words at this point.
286 	 */
287 	v = adrof1(STRshell, &aliases);
288 	if (v == 0) {
289 	    vp = lastsh;
290 	    vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
291 	    vp[1] = NULL;
292 #ifdef _PATH_BSHELL
293 	    if (fd != -1 && c != '#')
294 		vp[0] = STR_BSHELL;
295 #endif
296 	}
297 	else
298 	    vp = v->vec;
299 	st0 = st[0];
300 	st[0] = sf;
301 	ost = st;
302 	st = blkspl(vp, st);	/* Splice up the new arglst */
303 	ost[0] = st0;
304 	sf = *st;
305 	/* The order for the conversions is significant */
306 	t = short2blk(st);
307 	f = short2str(sf);
308 	xfree((ptr_t) st);
309 	Vt = t;
310 	(void) execve(f, t, environ);
311 	Vt = 0;
312 	blkfree((Char **) t);
313 	/* The sky is falling, the sky is falling! */
314 
315     case ENOMEM:
316 	stderror(ERR_SYSTEM, f, strerror(errno));
317 
318     case ENOENT:
319 	break;
320 
321     default:
322 	if (exerr == 0) {
323 	    exerr = strerror(errno);
324 	    if (expath)
325 		xfree((ptr_t) expath);
326 	    expath = Strsave(sf);
327 	    Vexpath = expath;
328 	}
329     }
330 }
331 
332 /*ARGSUSED*/
333 void
334 execash(t, kp)
335     Char  **t;
336     register struct command *kp;
337 {
338     int     saveIN, saveOUT, saveDIAG, saveSTD;
339     int     oSHIN;
340     int     oSHOUT;
341     int     oSHERR;
342     int     oOLDSTD;
343     jmp_buf osetexit;
344     int	    my_reenter;
345     int     odidfds;
346     sig_t   osigint, osigquit, osigterm;
347 
348     if (chkstop == 0 && setintr)
349 	panystop(0);
350     /*
351      * Hmm, we don't really want to do that now because we might
352      * fail, but what is the choice
353      */
354     rechist();
355 
356     osigint  = signal(SIGINT, parintr);
357     osigquit = signal(SIGQUIT, parintr);
358     osigterm = signal(SIGTERM, parterm);
359 
360     odidfds = didfds;
361     oSHIN = SHIN;
362     oSHOUT = SHOUT;
363     oSHERR = SHERR;
364     oOLDSTD = OLDSTD;
365 
366     saveIN = dcopy(SHIN, -1);
367     saveOUT = dcopy(SHOUT, -1);
368     saveDIAG = dcopy(SHERR, -1);
369     saveSTD = dcopy(OLDSTD, -1);
370 
371     lshift(kp->t_dcom, 1);
372 
373     getexit(osetexit);
374 
375     if ((my_reenter = setexit()) == 0) {
376 	SHIN = dcopy(0, -1);
377 	SHOUT = dcopy(1, -1);
378 	SHERR = dcopy(2, -1);
379 	didfds = 0;
380 	doexec(t, kp);
381     }
382 
383     (void) signal(SIGINT, osigint);
384     (void) signal(SIGQUIT, osigquit);
385     (void) signal(SIGTERM, osigterm);
386 
387     doneinp = 0;
388     didfds = odidfds;
389     (void) close(SHIN);
390     (void) close(SHOUT);
391     (void) close(SHERR);
392     (void) close(OLDSTD);
393     SHIN = dmove(saveIN, oSHIN);
394     SHOUT = dmove(saveOUT, oSHOUT);
395     SHERR = dmove(saveDIAG, oSHERR);
396     OLDSTD = dmove(saveSTD, oOLDSTD);
397 
398     resexit(osetexit);
399     if (my_reenter)
400 	stderror(ERR_SILENT);
401 }
402 
403 void
404 xechoit(t)
405     Char  **t;
406 {
407     if (adrof(STRecho)) {
408 	(void) fflush(csherr);
409 	blkpr(csherr, t);
410 	(void) fputc('\n', csherr);
411     }
412 }
413 
414 void
415 /*ARGSUSED*/
416 dohash(v, t)
417     Char **v;
418     struct command *t;
419 {
420     DIR    *dirp;
421     register struct dirent *dp;
422     register int cnt;
423     int     i = 0;
424     struct varent *pathv = adrof(STRpath);
425     Char  **pv;
426     int     hashval;
427 
428     havhash = 1;
429     for (cnt = 0; cnt < sizeof xhash; cnt++)
430 	xhash[cnt] = 0;
431     if (pathv == 0)
432 	return;
433     for (pv = pathv->vec; *pv; pv++, i++) {
434 	if (pv[0][0] != '/')
435 	    continue;
436 	dirp = opendir(short2str(*pv));
437 	if (dirp == NULL)
438 	    continue;
439 	while ((dp = readdir(dirp)) != NULL) {
440 	    if (dp->d_ino == 0)
441 		continue;
442 	    if (dp->d_name[0] == '.' &&
443 		(dp->d_name[1] == '\0' ||
444 		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
445 		continue;
446 	    hashval = hash(hashname(str2short(dp->d_name)), i);
447 	    bis(xhash, hashval);
448 	    /* tw_add_comm_name (dp->d_name); */
449 	}
450 	(void) closedir(dirp);
451     }
452 }
453 
454 void
455 /*ARGSUSED*/
456 dounhash(v, t)
457     Char **v;
458     struct command *t;
459 {
460     havhash = 0;
461 }
462 
463 void
464 /*ARGSUSED*/
465 hashstat(v, t)
466     Char **v;
467     struct command *t;
468 {
469     if (hits + misses)
470 	(void) fprintf(cshout, "%d hits, %d misses, %d%%\n",
471 		       hits, misses, 100 * hits / (hits + misses));
472 }
473 
474 /*
475  * Hash a command name.
476  */
477 static int
478 hashname(cp)
479     register Char *cp;
480 {
481     register long h = 0;
482 
483     while (*cp)
484 	h = hash(h, *cp++);
485     return ((int) h);
486 }
487 
488 static int
489 iscommand(name)
490     Char   *name;
491 {
492     register Char **pv;
493     register Char *sav;
494     register struct varent *v;
495     register bool slash = any(short2str(name), '/');
496     register int hashval = 0, hashval1, i;
497 
498     v = adrof(STRpath);
499     if (v == 0 || v->vec[0] == 0 || slash)
500 	pv = justabs;
501     else
502 	pv = v->vec;
503     sav = Strspl(STRslash, name);	/* / command name for postpending */
504     if (havhash)
505 	hashval = hashname(name);
506     i = 0;
507     do {
508 	if (!slash && pv[0][0] == '/' && havhash) {
509 	    hashval1 = hash(hashval, i);
510 	    if (!bit(xhash, hashval1))
511 		goto cont;
512 	}
513 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
514 	    if (executable(NULL, name, 0)) {
515 		xfree((ptr_t) sav);
516 		return i + 1;
517 	    }
518 	}
519 	else {
520 	    if (executable(*pv, sav, 0)) {
521 		xfree((ptr_t) sav);
522 		return i + 1;
523 	    }
524 	}
525 cont:
526 	pv++;
527 	i++;
528     } while (*pv);
529     xfree((ptr_t) sav);
530     return 0;
531 }
532 
533 /* Also by:
534  *  Andreas Luik <luik@isaak.isa.de>
535  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
536  *  Azenberstr. 35
537  *  D-7000 Stuttgart 1
538  *  West-Germany
539  * is the executable() routine below and changes to iscommand().
540  * Thanks again!!
541  */
542 
543 /*
544  * executable() examines the pathname obtained by concatenating dir and name
545  * (dir may be NULL), and returns 1 either if it is executable by us, or
546  * if dir_ok is set and the pathname refers to a directory.
547  * This is a bit kludgy, but in the name of optimization...
548  */
549 static int
550 executable(dir, name, dir_ok)
551     Char   *dir, *name;
552     bool    dir_ok;
553 {
554     struct stat stbuf;
555     Char    path[MAXPATHLEN + 1], *dp, *sp;
556     char   *strname;
557 
558     if (dir && *dir) {
559 	for (dp = path, sp = dir; *sp; *dp++ = *sp++)
560 	    if (dp == &path[MAXPATHLEN + 1]) {
561 		*--dp = '\0';
562 		break;
563 	    }
564 	for (sp = name; *sp; *dp++ = *sp++)
565 	    if (dp == &path[MAXPATHLEN + 1]) {
566 		*--dp = '\0';
567 		break;
568 	    }
569 	*dp = '\0';
570 	strname = short2str(path);
571     }
572     else
573 	strname = short2str(name);
574     return (stat(strname, &stbuf) != -1 &&
575 	    ((S_ISREG(stbuf.st_mode) &&
576     /* save time by not calling access() in the hopeless case */
577 	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
578 	      access(strname, X_OK) == 0) ||
579 	     (dir_ok && S_ISDIR(stbuf.st_mode))));
580 }
581 
582 /* The dowhich() is by:
583  *  Andreas Luik <luik@isaak.isa.de>
584  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
585  *  Azenberstr. 35
586  *  D-7000 Stuttgart 1
587  *  West-Germany
588  * Thanks!!
589  */
590 /*ARGSUSED*/
591 void
592 dowhich(v, c)
593     register Char **v;
594     struct command *c;
595 {
596     struct wordent lex[3];
597     struct varent *vp;
598 
599     lex[0].next = &lex[1];
600     lex[1].next = &lex[2];
601     lex[2].next = &lex[0];
602 
603     lex[0].prev = &lex[2];
604     lex[1].prev = &lex[0];
605     lex[2].prev = &lex[1];
606 
607     lex[0].word = STRNULL;
608     lex[2].word = STRret;
609 
610     while (*++v) {
611 	if ((vp = adrof1(*v, &aliases)) != NULL) {
612 	    (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
613 	    blkpr(cshout, vp->vec);
614 	    (void) fputc('\n', cshout);
615 	}
616 	else {
617 	    lex[1].word = *v;
618 	    tellmewhat(lex);
619 	}
620     }
621 }
622 
623 static void
624 tellmewhat(lex)
625     struct wordent *lex;
626 {
627     register int i;
628     register struct biltins *bptr;
629     register struct wordent *sp = lex->next;
630     bool    aliased = 0;
631     Char   *s0, *s1, *s2;
632     Char    qc;
633 
634     if (adrof1(sp->word, &aliases)) {
635 	alias(lex);
636 	sp = lex->next;
637 	aliased = 1;
638     }
639 
640     s0 = sp->word;		/* to get the memory freeing right... */
641 
642     /* handle quoted alias hack */
643     if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
644 	(sp->word)++;
645 
646     /* do quoting, if it hasn't been done */
647     s1 = s2 = sp->word;
648     while (*s2)
649 	switch (*s2) {
650 	case '\'':
651 	case '"':
652 	    qc = *s2++;
653 	    while (*s2 && *s2 != qc)
654 		*s1++ = *s2++ | QUOTE;
655 	    if (*s2)
656 		s2++;
657 	    break;
658 	case '\\':
659 	    if (*++s2)
660 		*s1++ = *s2++ | QUOTE;
661 	    break;
662 	default:
663 	    *s1++ = *s2++;
664 	}
665     *s1 = '\0';
666 
667     for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
668 	if (eq(sp->word, str2short(bptr->bname))) {
669 	    if (aliased)
670 		prlex(cshout, lex);
671 	    (void) fprintf(cshout, "%s: shell built-in command.\n",
672 			   vis_str(sp->word));
673 	    sp->word = s0;	/* we save and then restore this */
674 	    return;
675 	}
676     }
677 
678     if ((i = iscommand(strip(sp->word))) != 0) {
679 	register Char **pv;
680 	register struct varent *v;
681 	bool    slash = any(short2str(sp->word), '/');
682 
683 	v = adrof(STRpath);
684 	if (v == 0 || v->vec[0] == 0 || slash)
685 	    pv = justabs;
686 	else
687 	    pv = v->vec;
688 
689 	while (--i)
690 	    pv++;
691 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
692 	    sp->word = Strspl(STRdotsl, sp->word);
693 	    prlex(cshout, lex);
694 	    xfree((ptr_t) sp->word);
695 	    sp->word = s0;	/* we save and then restore this */
696 	    return;
697 	}
698 	s1 = Strspl(*pv, STRslash);
699 	sp->word = Strspl(s1, sp->word);
700 	xfree((ptr_t) s1);
701 	prlex(cshout, lex);
702 	xfree((ptr_t) sp->word);
703     }
704     else {
705 	if (aliased)
706 	    prlex(cshout, lex);
707 	(void) fprintf(csherr, "%s: Command not found.\n", vis_str(sp->word));
708     }
709     sp->word = s0;		/* we save and then restore this */
710 }
711