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