xref: /original-bsd/bin/csh/exec.c (revision ff2bc52d)
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.19 (Berkeley) 07/28/91";
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(short2str(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(short2str(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(short2str(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(v, t)
335     Char  **v;
336     register struct command *t;
337 {
338     if (chkstop == 0 && setintr)
339 	panystop(0);
340     rechist();
341     (void) signal(SIGINT, parintr);
342     (void) signal(SIGQUIT, parintr);
343     (void) signal(SIGTERM, parterm);	/* if doexec loses, screw */
344     lshift(t->t_dcom, 1);
345     exiterr = 1;
346     doexec(NULL, t);
347     /* NOTREACHED */
348 }
349 
350 void
351 xechoit(t)
352     Char  **t;
353 {
354     if (adrof(STRecho)) {
355 	(void) fflush(csherr);
356 	blkpr(csherr, t);
357 	(void) fputc('\n', csherr);
358     }
359 }
360 
361 void
362 /*ARGSUSED*/
363 dohash(v, t)
364     Char **v;
365     struct command *t;
366 {
367     DIR    *dirp;
368     register struct dirent *dp;
369     register int cnt;
370     int     i = 0;
371     struct varent *pathv = adrof(STRpath);
372     Char  **pv;
373     int     hashval;
374 
375     havhash = 1;
376     for (cnt = 0; cnt < sizeof xhash; cnt++)
377 	xhash[cnt] = 0;
378     if (pathv == 0)
379 	return;
380     for (pv = pathv->vec; *pv; pv++, i++) {
381 	if (pv[0][0] != '/')
382 	    continue;
383 	dirp = opendir(short2str(*pv));
384 	if (dirp == NULL)
385 	    continue;
386 	while ((dp = readdir(dirp)) != NULL) {
387 	    if (dp->d_ino == 0)
388 		continue;
389 	    if (dp->d_name[0] == '.' &&
390 		(dp->d_name[1] == '\0' ||
391 		 dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
392 		continue;
393 	    hashval = hash(hashname(str2short(dp->d_name)), i);
394 	    bis(xhash, hashval);
395 	    /* tw_add_comm_name (dp->d_name); */
396 	}
397 	(void) closedir(dirp);
398     }
399 }
400 
401 void
402 /*ARGSUSED*/
403 dounhash(v, t)
404     Char **v;
405     struct command *t;
406 {
407     havhash = 0;
408 }
409 
410 void
411 /*ARGSUSED*/
412 hashstat(v, t)
413     Char **v;
414     struct command *t;
415 {
416     if (hits + misses)
417 	(void) fprintf(cshout, "%d hits, %d misses, %d%%\n",
418 		       hits, misses, 100 * hits / (hits + misses));
419 }
420 
421 /*
422  * Hash a command name.
423  */
424 static int
425 hashname(cp)
426     register Char *cp;
427 {
428     register long h = 0;
429 
430     while (*cp)
431 	h = hash(h, *cp++);
432     return ((int) h);
433 }
434 
435 static int
436 iscommand(name)
437     Char   *name;
438 {
439     register Char **pv;
440     register Char *sav;
441     register struct varent *v;
442     register bool slash = any(short2str(name), '/');
443     register int hashval = 0, hashval1, i;
444 
445     v = adrof(STRpath);
446     if (v == 0 || v->vec[0] == 0 || slash)
447 	pv = justabs;
448     else
449 	pv = v->vec;
450     sav = Strspl(STRslash, name);	/* / command name for postpending */
451     if (havhash)
452 	hashval = hashname(name);
453     i = 0;
454     do {
455 	if (!slash && pv[0][0] == '/' && havhash) {
456 	    hashval1 = hash(hashval, i);
457 	    if (!bit(xhash, hashval1))
458 		goto cont;
459 	}
460 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
461 	    if (executable(NULL, name, 0)) {
462 		xfree((ptr_t) sav);
463 		return i + 1;
464 	    }
465 	}
466 	else {
467 	    if (executable(*pv, sav, 0)) {
468 		xfree((ptr_t) sav);
469 		return i + 1;
470 	    }
471 	}
472 cont:
473 	pv++;
474 	i++;
475     } while (*pv);
476     xfree((ptr_t) sav);
477     return 0;
478 }
479 
480 /* Also by:
481  *  Andreas Luik <luik@isaak.isa.de>
482  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
483  *  Azenberstr. 35
484  *  D-7000 Stuttgart 1
485  *  West-Germany
486  * is the executable() routine below and changes to iscommand().
487  * Thanks again!!
488  */
489 
490 /*
491  * executable() examines the pathname obtained by concatenating dir and name
492  * (dir may be NULL), and returns 1 either if it is executable by us, or
493  * if dir_ok is set and the pathname refers to a directory.
494  * This is a bit kludgy, but in the name of optimization...
495  */
496 static int
497 executable(dir, name, dir_ok)
498     Char   *dir, *name;
499     bool    dir_ok;
500 {
501     struct stat stbuf;
502     Char    path[MAXPATHLEN + 1], *dp, *sp;
503     char   *strname;
504 
505     if (dir && *dir) {
506 	for (dp = path, sp = dir; *sp; *dp++ = *sp++)
507 	    if (dp == &path[MAXPATHLEN + 1]) {
508 		*--dp = '\0';
509 		break;
510 	    }
511 	for (sp = name; *sp; *dp++ = *sp++)
512 	    if (dp == &path[MAXPATHLEN + 1]) {
513 		*--dp = '\0';
514 		break;
515 	    }
516 	*dp = '\0';
517 	strname = short2str(path);
518     }
519     else
520 	strname = short2str(name);
521     return (stat(strname, &stbuf) != -1 &&
522 	    ((S_ISREG(stbuf.st_mode) &&
523     /* save time by not calling access() in the hopeless case */
524 	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
525 	      access(strname, X_OK) == 0) ||
526 	     (dir_ok && S_ISDIR(stbuf.st_mode))));
527 }
528 
529 /* The dowhich() is by:
530  *  Andreas Luik <luik@isaak.isa.de>
531  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
532  *  Azenberstr. 35
533  *  D-7000 Stuttgart 1
534  *  West-Germany
535  * Thanks!!
536  */
537 /*ARGSUSED*/
538 void
539 dowhich(v, c)
540     register Char **v;
541     struct command *c;
542 {
543     struct wordent lex[3];
544     struct varent *vp;
545 
546     lex[0].next = &lex[1];
547     lex[1].next = &lex[2];
548     lex[2].next = &lex[0];
549 
550     lex[0].prev = &lex[2];
551     lex[1].prev = &lex[0];
552     lex[2].prev = &lex[1];
553 
554     lex[0].word = STRNULL;
555     lex[2].word = STRret;
556 
557     while (*++v) {
558 	if (vp = adrof1(*v, &aliases)) {
559 	    (void) fprintf(cshout, "%s: \t aliased to ", short2str(*v));
560 	    blkpr(cshout, vp->vec);
561 	    (void) fputc('\n', cshout);
562 	}
563 	else {
564 	    lex[1].word = *v;
565 	    tellmewhat(lex);
566 	}
567     }
568 }
569 
570 static void
571 tellmewhat(lex)
572     struct wordent *lex;
573 {
574     register int i;
575     register struct biltins *bptr;
576     register struct wordent *sp = lex->next;
577     bool    aliased = 0;
578     Char   *s0, *s1, *s2;
579     Char    qc;
580 
581     if (adrof1(sp->word, &aliases)) {
582 	alias(lex);
583 	sp = lex->next;
584 	aliased = 1;
585     }
586 
587     s0 = sp->word;		/* to get the memory freeing right... */
588 
589     /* handle quoted alias hack */
590     if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
591 	(sp->word)++;
592 
593     /* do quoting, if it hasn't been done */
594     s1 = s2 = sp->word;
595     while (*s2)
596 	switch (*s2) {
597 	case '\'':
598 	case '"':
599 	    qc = *s2++;
600 	    while (*s2 && *s2 != qc)
601 		*s1++ = *s2++ | QUOTE;
602 	    if (*s2)
603 		s2++;
604 	    break;
605 	case '\\':
606 	    if (*++s2)
607 		*s1++ = *s2++ | QUOTE;
608 	    break;
609 	default:
610 	    *s1++ = *s2++;
611 	}
612     *s1 = '\0';
613 
614     for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
615 	if (eq(sp->word, str2short(bptr->bname))) {
616 	    if (aliased)
617 		prlex(cshout, lex);
618 	    (void) fprintf(cshout, "%s: shell built-in command.\n",
619 			   short2str(sp->word));
620 	    sp->word = s0;	/* we save and then restore this */
621 	    return;
622 	}
623     }
624 
625     if (i = iscommand(strip(sp->word))) {
626 	register Char **pv;
627 	register struct varent *v;
628 	bool    slash = any(short2str(sp->word), '/');
629 
630 	v = adrof(STRpath);
631 	if (v == 0 || v->vec[0] == 0 || slash)
632 	    pv = justabs;
633 	else
634 	    pv = v->vec;
635 
636 	while (--i)
637 	    pv++;
638 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
639 	    sp->word = Strspl(STRdotsl, sp->word);
640 	    prlex(cshout, lex);
641 	    xfree((ptr_t) sp->word);
642 	    sp->word = s0;	/* we save and then restore this */
643 	    return;
644 	}
645 	s1 = Strspl(*pv, STRslash);
646 	sp->word = Strspl(s1, sp->word);
647 	xfree((ptr_t) s1);
648 	prlex(cshout, lex);
649 	xfree((ptr_t) sp->word);
650     }
651     else {
652 	if (aliased)
653 	    prlex(cshout, lex);
654 	(void) fprintf(csherr, "%s: Command not found.\n", short2str(sp->word));
655     }
656     sp->word = s0;		/* we save and then restore this */
657 }
658