OpenSolaris_b135/cmd/csh/sh.exec.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "sh.h"
#include <dirent.h>
#include <string.h>
#include "sh.tconst.h"
#include "sh_policy.h"


/*
 * C shell
 */

/*
 * System level search and execute of a command.
 * We look in each directory for the specified command name.
 * If the name contains a '/' then we execute only the full path name.
 * If there is no search path then we execute only full path names.
 */

/*
 * As we search for the command we note the first non-trivial error
 * message for presentation to the user.  This allows us often
 * to show that a file has the wrong mode/no access when the file
 * is not in the last component of the search path, so we must
 * go on after first detecting the error.
 */
char *exerr;			/* Execution error message */

void	pexerr(void);
void	texec(struct command *, tchar *, tchar **);
void	xechoit(tchar **);
void	dohash(char []);

static void	tconvert(struct command *, tchar *, tchar **);


extern DIR *opendir_(tchar *);

void
doexec(struct command *t)
{
	tchar *sav;
	tchar *dp, **pv, **av;
	struct varent *v;
	bool slash;
	int hashval, hashval1, i;
	tchar *blk[2];
#ifdef TRACE
	tprintf("TRACE- doexec()\n");
#endif

	/*
	 * Glob the command name.  If this does anything, then we
	 * will execute the command only relative to ".".  One special
	 * case: if there is no PATH, then we execute only commands
	 * which start with '/'.
	 */
	dp = globone(t->t_dcom[0]);
	sav = t->t_dcom[0];
	exerr = 0; t->t_dcom[0] = dp;
	setname(dp);
	xfree(sav);
	v = adrof(S_path /* "path" */);
	if (v == 0 && dp[0] != '/') {
		pexerr();
	}
	slash = gflag;

	/*
	 * Glob the argument list, if necessary.
	 * Otherwise trim off the quote bits.
	 */
	gflag = 0; av = &t->t_dcom[1];
	tglob(av);
	if (gflag) {
		av = glob(av);
		if (av == 0)
			error("No match");
	}
	blk[0] = t->t_dcom[0];
	blk[1] = 0;
	av = blkspl(blk, av);
#ifdef VFORK
	Vav = av;
#endif
	trim(av);
	slash |= any('/', av[0]);

	xechoit(av);		/* Echo command if -x */
	/*
	 * Since all internal file descriptors are set to close on exec,
	 * we don't need to close them explicitly here.  Just reorient
	 * ourselves for error messages.
	 */
	SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0;

	/*
	 * We must do this AFTER any possible forking (like `foo`
	 * in glob) so that this shell can still do subprocesses.
	 */
	(void) sigsetmask(0);

	/*
	 * If no path, no words in path, or a / in the filename
	 * then restrict the command search.
	 */
	if (v == 0 || v->vec[0] == 0 || slash)
		pv = justabs;
	else
		pv = v->vec;
	sav = strspl(S_SLASH /* "/" */, *av); /* / command name for postpending */
#ifdef VFORK
	Vsav = sav;
#endif
	if (havhash)
		hashval = hashname(*av);
	i = 0;
#ifdef VFORK
	hits++;
#endif
	do {
		if (!slash && pv[0][0] == '/' && havhash) {
			hashval1 = hash(hashval, i);
			if (!bit(xhash, hashval1))
				goto cont;
		}

		if (pv[0][0] == 0 || eq(pv[0], S_DOT /* "." */)) { /* don't make ./xxx */
			texec(t, *av, av);
		} else {
			dp = strspl(*pv, sav);
#ifdef VFORK
			Vdp = dp;
#endif
			texec(t, dp, av);
#ifdef VFORK
			Vdp = 0;
#endif
			xfree(dp);
		}
#ifdef VFORK
		misses++;
#endif
cont:
		pv++;
		i++;
	} while (*pv);
#ifdef VFORK
	hits--;
#endif
#ifdef VFORK
	Vsav = 0;
	Vav = 0;
#endif
	xfree(sav);
	xfree((char *)av);
	pexerr();
}

void
pexerr(void)
{

#ifdef TRACE
	tprintf("TRACE- pexerr()\n");
#endif
	/* Couldn't find the damn thing */
	if (exerr)
		bferr(exerr);
	bferr("Command not found");
}

/*
 * Execute command f, arg list t.
 * Record error message if not found.
 * Also do shell scripts here.
 */
void
texec(struct command *cmd, tchar *f, tchar **t)
{
	int	pfstatus = 0;
	struct	varent *v;
	tchar	**vp;
	tchar		*lastsh[2];

#ifdef TRACE
	tprintf("TRACE- texec()\n");
#endif
	/* convert cfname and cargs from tchar to char */
	tconvert(cmd, f, t);

	if (pfcshflag == 1) {
		pfstatus = secpolicy_pfexec((const char *)(cmd->cfname),
		    cmd->cargs, (const char **)NULL);
		if (pfstatus != NOATTRS) {
			errno = pfstatus;
		}
	}
	if ((pfcshflag == 0) || (pfstatus == NOATTRS)) {
		execv(cmd->cfname, cmd->cargs);
	}

	/*
	 * exec returned, free up allocations from above
	 * tconvert(), zero cfname and cargs to prevent
	 * duplicate free() in freesyn()
	 */
	xfree(cmd->cfname);
	chr_blkfree(cmd->cargs);
	cmd->cfname = (char *)0;
	cmd->cargs = (char **)0;

	switch (errno) {
	case ENOEXEC:
		/* check that this is not a binary file */
		{
			int ff = open_(f, 0);
			tchar ch[MB_LEN_MAX];

			if (ff != -1 && read_(ff, ch, 1) == 1 &&
			    !isprint(ch[0]) && !isspace(ch[0])) {
				printf("Cannot execute binary file.\n");
				Perror(f);
				(void) close(ff);
				unsetfd(ff);
				return;
			}
			(void) close(ff);
			unsetfd(ff);
		}
		/*
		 * If there is an alias for shell, then
		 * put the words of the alias in front of the
		 * argument list replacing the command name.
		 * Note no interpretation of the words at this point.
		 */
		v = adrof1(S_shell /* "shell" */, &aliases);
		if (v == 0) {
#ifdef OTHERSH
			int ff = open_(f, 0);
			tchar ch[MB_LEN_MAX];
#endif

			vp = lastsh;
			vp[0] = adrof(S_shell /* "shell" */) ? value(S_shell /* "shell" */) : S_SHELLPATH /* SHELLPATH */;
			vp[1] =  (tchar *) NULL;
#ifdef OTHERSH
			if (ff != -1 && read_(ff, ch, 1) == 1 && ch[0] != '#')
				vp[0] = S_OTHERSH /* OTHERSH */;
			(void) close(ff);
			unsetfd(ff);
#endif
		} else
			vp = v->vec;
		t[0] = f;
		t = blkspl(vp, t);		/* Splice up the new arglst */
		f = *t;

		tconvert(cmd, f, t);		/* convert tchar to char */

		/*
		 * now done with tchar arg list t,
		 * free the space calloc'd by above blkspl()
		 */
		xfree((char *)t);

		execv(cmd->cfname, cmd->cargs);	/* exec the command */

		/* exec returned, same free'ing as above */
		xfree(cmd->cfname);
		chr_blkfree(cmd->cargs);
		cmd->cfname = (char *)0;
		cmd->cargs = (char **)0;

		/* The sky is falling, the sky is falling! */

	case ENOMEM:
		Perror(f);

	case ENOENT:
		break;

	default:
		if (exerr == 0) {
			exerr = strerror(errno);
			setname(f);
		}
	}
}


static void
tconvert(struct command *cmd, tchar *fname, tchar **list)
{
	char **rc;
	int len;

	cmd->cfname = tstostr(NULL, fname);

	len = blklen(list);
	rc = cmd->cargs = (char **)
		xcalloc((uint_t)(len + 1), sizeof (char **));
	while (len--)
		*rc++ = tstostr(NULL, *list++);
	*rc = NULL;
}


/*ARGSUSED*/
void
execash(tchar **t, struct command *kp)
{
#ifdef TRACE
	tprintf("TRACE- execash()\n");
#endif

	rechist();
	(void) signal(SIGINT, parintr);
	(void) signal(SIGQUIT, parintr);
	(void) signal(SIGTERM, parterm);	/* if doexec loses, screw */
	lshift(kp->t_dcom, 1);
	exiterr++;
	doexec(kp);
	/*NOTREACHED*/
}

void
xechoit(tchar **t)
{
#ifdef TRACE
	tprintf("TRACE- xechoit()\n");
#endif

	if (adrof(S_echo /* "echo" */)) {
		flush();
		haderr = 1;
		blkpr(t), Putchar('\n');
		haderr = 0;
	}
}

/*
 * This routine called when user enters "rehash".
 * Both the path and cdpath caching arrays will
 * be rehashed, via calling dohash.  If either
 * variable is not set with a value, then dohash
 * just exits.
 */
void
dorehash(void)
{
	dohash(xhash);
	dohash(xhash2);
}

/*
 * Fill up caching arrays for path and cdpath
 */
void
dohash(char cachearray[])
{
	struct stat stb;
	DIR *dirp;
	struct dirent *dp;
	int cnt;
	int i = 0;
	struct varent *v;
	tchar **pv;
	int hashval;
	tchar curdir_[MAXNAMLEN+1];

#ifdef TRACE
	tprintf("TRACE- dohash()\n");
#endif
	/* Caching $path */
	if (cachearray == xhash) {
		havhash = 1;
		v = adrof(S_path /* "path" */);
	} else {    /* Caching $cdpath */
		havhash2 = 1;
		v = adrof(S_cdpath /* "cdpath" */);
	}

	for (cnt = 0; cnt < (HSHSIZ / 8); cnt++)
		cachearray[cnt] = 0;
	if (v == 0)
		{
		return;
		}
	for (pv = v->vec; *pv; pv++, i++) {
		if (pv[0][0] != '/')
			continue;
		dirp = opendir_(*pv);
		if (dirp == NULL)
			continue;
		if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) {
			unsetfd(dirp->dd_fd);
			closedir_(dirp);
			continue;
		}
		while ((dp = readdir(dirp)) != NULL) {
			if (dp->d_ino == 0)
				continue;
			if (dp->d_name[0] == '.' &&
			    (dp->d_name[1] == '\0' ||
			    dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
				continue;
			hashval = hash(hashname(strtots(curdir_, dp->d_name)), i);
			bis(cachearray, hashval);
		}
		unsetfd(dirp->dd_fd);
		closedir_(dirp);
	}
}

void
dounhash(void)
{

#ifdef TRACE
	tprintf("TRACE- dounhash()\n");
#endif
	havhash = 0;
	havhash2 = 0;
}

#ifdef VFORK
void
hashstat(void)
{
#ifdef TRACE
	tprintf("TRACE- hashstat_()\n");
#endif

	if (hits+misses)
		printf("%d hits, %d misses, %d%%\n",
			hits, misses, 100 * hits / (hits + misses));
}
#endif

/*
 * Hash a command name.
 */
int
hashname(tchar *cp)
{
	long h = 0;

#ifdef TRACE
	tprintf("TRACE- hashname()\n");
#endif
	while (*cp)
		h = hash(h, *cp++);
	return ((int)h);
}