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