xref: /original-bsd/bin/csh/exec.c (revision 5da3d24a)
1 /*-
2  * Copyright (c) 1980, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)exec.c	5.17 (Berkeley) 06/17/91";
10 #endif /* not lint */
11 
12 #include <sys/types.h>
13 #include <dirent.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #if __STDC__
20 # include <stdarg.h>
21 #else
22 # include <varargs.h>
23 #endif
24 
25 #include "csh.h"
26 #include "extern.h"
27 
28 /*
29  * System level search and execute of a command.  We look in each directory
30  * for the specified command name.  If the name contains a '/' then we
31  * execute only the full path name.  If there is no search path then we
32  * execute only full path names.
33  */
34 extern char **environ;
35 
36 /*
37  * As we search for the command we note the first non-trivial error
38  * message for presentation to the user.  This allows us often
39  * to show that a file has the wrong mode/no access when the file
40  * is not in the last component of the search path, so we must
41  * go on after first detecting the error.
42  */
43 static char *exerr;		/* Execution error message */
44 static Char *expath;		/* Path for exerr */
45 
46 /*
47  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
48  * to hash execs.  If it is allocated (havhash true), then to tell
49  * whether ``name'' is (possibly) present in the i'th component
50  * of the variable path, you look at the bit in xhash indexed by
51  * hash(hashname("name"), i).  This is setup automatically
52  * after .login is executed, and recomputed whenever ``path'' is
53  * changed.
54  * The two part hash function is designed to let texec() call the
55  * more expensive hashname() only once and the simple hash() several
56  * times (once for each path component checked).
57  * Byte size is assumed to be 8.
58  */
59 #define	HSHSIZ		8192	/* 1k bytes */
60 #define HSHMASK		(HSHSIZ - 1)
61 #define HSHMUL		243
62 static char xhash[HSHSIZ / 8];
63 
64 #define hash(a, b)	((a) * HSHMUL + (b) & HSHMASK)
65 #define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
66 #define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
67 static int hits, misses;
68 
69 /* Dummy search path for just absolute search when no path */
70 static Char *justabs[] = {STRNULL, 0};
71 
72 static void	pexerr __P((void));
73 static void	texec __P((Char *, Char **));
74 static int	hashname __P((Char *));
75 
76 void
77 doexec(t)
78     register struct command *t;
79 {
80     register Char *dp, **pv, **av, *sav;
81     register struct varent *v;
82     register bool slash;
83     register int hashval = 0, hashval1, i;
84     Char   *blk[2];
85 
86     /*
87      * Glob the command name. We will search $path even if this does something,
88      * as in sh but not in csh.  One special case: if there is no PATH, then we
89      * execute only commands which start with '/'.
90      */
91     blk[0] = t->t_dcom[0];
92     blk[1] = 0;
93     gflag = 0, tglob(blk);
94     if (gflag) {
95 	pv = globall(blk);
96 	if (pv == 0) {
97 	    setname(short2str(blk[0]));
98 	    stderror(ERR_NAME | ERR_NOMATCH);
99 	}
100 	gargv = 0;
101     }
102     else
103 	pv = saveblk(blk);
104 
105     trim(pv);
106 
107     exerr = 0;
108     expath = Strsave(pv[0]);
109     Vexpath = expath;
110 
111     v = adrof(STRpath);
112     if (v == 0 && expath[0] != '/') {
113 	blkfree(pv);
114 	pexerr();
115     }
116     slash = any(short2str(expath), '/');
117 
118     /*
119      * Glob the argument list, if necessary. Otherwise trim off the quote bits.
120      */
121     gflag = 0;
122     av = &t->t_dcom[1];
123     tglob(av);
124     if (gflag) {
125 	av = globall(av);
126 	if (av == 0) {
127 	    blkfree(pv);
128 	    setname(short2str(expath));
129 	    stderror(ERR_NAME | ERR_NOMATCH);
130 	}
131 	gargv = 0;
132     }
133     else
134 	av = saveblk(av);
135 
136     blkfree(t->t_dcom);
137     t->t_dcom = blkspl(pv, av);
138     xfree((ptr_t) pv);
139     xfree((ptr_t) av);
140     av = t->t_dcom;
141     trim(av);
142 
143     if (*av == NULL || **av == '\0')
144 	pexerr();
145 
146     xechoit(av);		/* Echo command if -x */
147     /*
148      * Since all internal file descriptors are set to close on exec, we don't
149      * need to close them explicitly here.  Just reorient ourselves for error
150      * messages.
151      */
152     SHIN = 0;
153     SHOUT = 1;
154     SHDIAG = 2;
155     OLDSTD = 0;
156     /*
157      * We must do this AFTER any possible forking (like `foo` in glob) so that
158      * this shell can still do subprocesses.
159      */
160     (void) sigsetmask((sigset_t) 0);
161     /*
162      * If no path, no words in path, or a / in the filename then restrict the
163      * command search.
164      */
165     if (v == 0 || v->vec[0] == 0 || slash)
166 	pv = justabs;
167     else
168 	pv = v->vec;
169     sav = Strspl(STRslash, *av);/* / command name for postpending */
170     Vsav = sav;
171     if (havhash)
172 	hashval = hashname(*av);
173     i = 0;
174     hits++;
175     do {
176 	/*
177 	 * Try to save time by looking at the hash table for where this command
178 	 * could be.  If we are doing delayed hashing, then we put the names in
179 	 * one at a time, as the user enters them.  This is kinda like Korn
180 	 * Shell's "tracked aliases".
181 	 */
182 	if (!slash && pv[0][0] == '/' && havhash) {
183 	    hashval1 = hash(hashval, i);
184 	    if (!bit(xhash, hashval1))
185 		goto cont;
186 	}
187 	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
188 	    texec(*av, av);
189 	else {
190 	    dp = Strspl(*pv, sav);
191 	    Vdp = dp;
192 	    texec(dp, av);
193 	    Vdp = 0;
194 	    xfree((ptr_t) dp);
195 	}
196 	misses++;
197 cont:
198 	pv++;
199 	i++;
200     } while (*pv);
201     hits--;
202     Vsav = 0;
203     xfree((ptr_t) sav);
204     pexerr();
205 }
206 
207 static void
208 pexerr()
209 {
210     /* Couldn't find the damn thing */
211     if (expath) {
212 	setname(short2str(expath));
213 	Vexpath = 0;
214 	xfree((ptr_t) expath);
215 	expath = 0;
216     }
217     else
218 	setname("");
219     if (exerr)
220 	stderror(ERR_NAME | ERR_STRING, exerr);
221     stderror(ERR_NAME | ERR_COMMAND);
222 }
223 
224 /*
225  * Execute command f, arg list t.
226  * Record error message if not found.
227  * Also do shell scripts here.
228  */
229 static void
230 texec(sf, st)
231     Char   *sf;
232     register Char **st;
233 {
234     register char **t;
235     register char *f;
236     register struct varent *v;
237     register Char **vp;
238     Char   *lastsh[2];
239     int     fd;
240     unsigned char c;
241     Char   *st0, **ost;
242 
243     /* The order for the conversions is significant */
244     t = short2blk(st);
245     f = short2str(sf);
246     Vt = t;
247     errno = 0;			/* don't use a previous error */
248     (void) execve(f, t, environ);
249     Vt = 0;
250     blkfree((Char **) t);
251     switch (errno) {
252 
253     case ENOEXEC:
254 	/*
255 	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
256 	 * it, don't feed it to the shell if it looks like a binary!
257 	 */
258 	if ((fd = open(f, O_RDONLY)) != -1) {
259 	    if (read(fd, (char *) &c, 1) == 1) {
260 		if (!Isprint(c) && (c != '\n' && c != '\t')) {
261 		    (void) close(fd);
262 		    /*
263 		     * We *know* what ENOEXEC means.
264 		     */
265 		    stderror(ERR_ARCH, f, strerror(errno));
266 		}
267 	    }
268 #ifdef _PATH_BSHELL
269 	    else
270 		c = '#';
271 #endif
272 	    (void) close(fd);
273 	}
274 	/*
275 	 * If there is an alias for shell, then put the words of the alias in
276 	 * front of the argument list replacing the command name. Note no
277 	 * interpretation of the words at this point.
278 	 */
279 	v = adrof1(STRshell, &aliases);
280 	if (v == 0) {
281 	    vp = lastsh;
282 	    vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
283 	    vp[1] = NULL;
284 #ifdef _PATH_BSHELL
285 	    if (fd != -1 && c != '#')
286 		vp[0] = STR_BSHELL;
287 #endif
288 	}
289 	else
290 	    vp = v->vec;
291 	st0 = st[0];
292 	st[0] = sf;
293 	ost = st;
294 	st = blkspl(vp, st);	/* Splice up the new arglst */
295 	ost[0] = st0;
296 	sf = *st;
297 	/* The order for the conversions is significant */
298 	t = short2blk(st);
299 	f = short2str(sf);
300 	xfree((ptr_t) st);
301 	Vt = t;
302 	(void) execve(f, t, environ);
303 	Vt = 0;
304 	blkfree((Char **) t);
305 	/* The sky is falling, the sky is falling! */
306 
307     case ENOMEM:
308 	stderror(ERR_SYSTEM, f, strerror(errno));
309 
310     case ENOENT:
311 	break;
312 
313     default:
314 	if (exerr == 0) {
315 	    exerr = strerror(errno);
316 	    if (expath)
317 		xfree((ptr_t) expath);
318 	    expath = Strsave(sf);
319 	    Vexpath = expath;
320 	}
321     }
322 }
323 
324 /*ARGSUSED*/
325 void
326 execash(t, kp)
327     char  **t;
328     register struct command *kp;
329 {
330     if (chkstop == 0 && setintr)
331 	panystop(0);
332     rechist();
333     (void) signal(SIGINT, parintr);
334     (void) signal(SIGQUIT, parintr);
335     (void) signal(SIGTERM, parterm);	/* if doexec loses, screw */
336     lshift(kp->t_dcom, 1);
337     exiterr = 1;
338     doexec(kp);
339     /* NOTREACHED */
340 }
341 
342 void
343 xechoit(t)
344     Char  **t;
345 {
346     if (adrof(STRecho)) {
347 	flush();
348 	haderr = 1;
349 	blkpr(t), xputchar('\n');
350 	haderr = 0;
351     }
352 }
353 
354 /*VARARGS0*/
355 void
356 dohash()
357 {
358     DIR    *dirp;
359     register struct dirent *dp;
360     register int cnt;
361     int     i = 0;
362     struct varent *v = adrof(STRpath);
363     Char  **pv;
364     int     hashval;
365 
366     havhash = 1;
367     for (cnt = 0; cnt < sizeof xhash; cnt++)
368 	xhash[cnt] = 0;
369     if (v == 0)
370 	return;
371     for (pv = v->vec; *pv; pv++, i++) {
372 	if (pv[0][0] != '/')
373 	    continue;
374 	dirp = opendir(short2str(*pv));
375 	if (dirp == NULL)
376 	    continue;
377 	while ((dp = readdir(dirp)) != NULL) {
378 	    if (dp->d_ino == 0)
379 		continue;
380 	    if (dp->d_name[0] == '.' &&
381 		(dp->d_name[1] == '\0' ||
382 		 dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
383 		continue;
384 	    hashval = hash(hashname(str2short(dp->d_name)), i);
385 	    bis(xhash, hashval);
386 	    /* tw_add_comm_name (dp->d_name); */
387 	}
388 	(void) closedir(dirp);
389     }
390 }
391 
392 void
393 dounhash()
394 {
395     havhash = 0;
396 }
397 
398 void
399 hashstat()
400 {
401     if (hits + misses)
402 	xprintf("%d hits, %d misses, %d%%\n",
403 		hits, misses, 100 * hits / (hits + misses));
404 }
405 
406 /*
407  * Hash a command name.
408  */
409 static int
410 hashname(cp)
411     register Char *cp;
412 {
413     register long h = 0;
414 
415     while (*cp)
416 	h = hash(h, *cp++);
417     return ((int) h);
418 }
419