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