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