Home | History | Annotate | Download | only in csh
      1 /*
      2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1980 Regents of the University of California.
     11  * All rights reserved.  The Berkeley Software License Agreement
     12  * specifies the terms and conditions for redistribution.
     13  */
     14 
     15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16 
     17 #include "sh.h"
     18 #include <dirent.h>
     19 #include <string.h>
     20 #include "sh.tconst.h"
     21 #include "sh_policy.h"
     22 
     23 
     24 /*
     25  * C shell
     26  */
     27 
     28 /*
     29  * System level search and execute of a command.
     30  * We look in each directory for the specified command name.
     31  * If the name contains a '/' then we execute only the full path name.
     32  * If there is no search path then we execute only full path names.
     33  */
     34 
     35 /*
     36  * As we search for the command we note the first non-trivial error
     37  * message for presentation to the user.  This allows us often
     38  * to show that a file has the wrong mode/no access when the file
     39  * is not in the last component of the search path, so we must
     40  * go on after first detecting the error.
     41  */
     42 char *exerr;			/* Execution error message */
     43 
     44 void	pexerr(void);
     45 void	texec(struct command *, tchar *, tchar **);
     46 void	xechoit(tchar **);
     47 void	dohash(char []);
     48 
     49 static void	tconvert(struct command *, tchar *, tchar **);
     50 
     51 
     52 extern DIR *opendir_(tchar *);
     53 
     54 void
     55 doexec(struct command *t)
     56 {
     57 	tchar *sav;
     58 	tchar *dp, **pv, **av;
     59 	struct varent *v;
     60 	bool slash;
     61 	int hashval, hashval1, i;
     62 	tchar *blk[2];
     63 #ifdef TRACE
     64 	tprintf("TRACE- doexec()\n");
     65 #endif
     66 
     67 	/*
     68 	 * Glob the command name.  If this does anything, then we
     69 	 * will execute the command only relative to ".".  One special
     70 	 * case: if there is no PATH, then we execute only commands
     71 	 * which start with '/'.
     72 	 */
     73 	dp = globone(t->t_dcom[0]);
     74 	sav = t->t_dcom[0];
     75 	exerr = 0; t->t_dcom[0] = dp;
     76 	setname(dp);
     77 	xfree(sav);
     78 	v = adrof(S_path /* "path" */);
     79 	if (v == 0 && dp[0] != '/') {
     80 		pexerr();
     81 	}
     82 	slash = gflag;
     83 
     84 	/*
     85 	 * Glob the argument list, if necessary.
     86 	 * Otherwise trim off the quote bits.
     87 	 */
     88 	gflag = 0; av = &t->t_dcom[1];
     89 	tglob(av);
     90 	if (gflag) {
     91 		av = glob(av);
     92 		if (av == 0)
     93 			error("No match");
     94 	}
     95 	blk[0] = t->t_dcom[0];
     96 	blk[1] = 0;
     97 	av = blkspl(blk, av);
     98 #ifdef VFORK
     99 	Vav = av;
    100 #endif
    101 	trim(av);
    102 	slash |= any('/', av[0]);
    103 
    104 	xechoit(av);		/* Echo command if -x */
    105 	/*
    106 	 * Since all internal file descriptors are set to close on exec,
    107 	 * we don't need to close them explicitly here.  Just reorient
    108 	 * ourselves for error messages.
    109 	 */
    110 	SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0;
    111 
    112 	/*
    113 	 * We must do this AFTER any possible forking (like `foo`
    114 	 * in glob) so that this shell can still do subprocesses.
    115 	 */
    116 	(void) sigsetmask(0);
    117 
    118 	/*
    119 	 * If no path, no words in path, or a / in the filename
    120 	 * then restrict the command search.
    121 	 */
    122 	if (v == 0 || v->vec[0] == 0 || slash)
    123 		pv = justabs;
    124 	else
    125 		pv = v->vec;
    126 	sav = strspl(S_SLASH /* "/" */, *av); /* / command name for postpending */
    127 #ifdef VFORK
    128 	Vsav = sav;
    129 #endif
    130 	if (havhash)
    131 		hashval = hashname(*av);
    132 	i = 0;
    133 #ifdef VFORK
    134 	hits++;
    135 #endif
    136 	do {
    137 		if (!slash && pv[0][0] == '/' && havhash) {
    138 			hashval1 = hash(hashval, i);
    139 			if (!bit(xhash, hashval1))
    140 				goto cont;
    141 		}
    142 
    143 		if (pv[0][0] == 0 || eq(pv[0], S_DOT /* "." */)) { /* don't make ./xxx */
    144 			texec(t, *av, av);
    145 		} else {
    146 			dp = strspl(*pv, sav);
    147 #ifdef VFORK
    148 			Vdp = dp;
    149 #endif
    150 			texec(t, dp, av);
    151 #ifdef VFORK
    152 			Vdp = 0;
    153 #endif
    154 			xfree(dp);
    155 		}
    156 #ifdef VFORK
    157 		misses++;
    158 #endif
    159 cont:
    160 		pv++;
    161 		i++;
    162 	} while (*pv);
    163 #ifdef VFORK
    164 	hits--;
    165 #endif
    166 #ifdef VFORK
    167 	Vsav = 0;
    168 	Vav = 0;
    169 #endif
    170 	xfree(sav);
    171 	xfree((char *)av);
    172 	pexerr();
    173 }
    174 
    175 void
    176 pexerr(void)
    177 {
    178 
    179 #ifdef TRACE
    180 	tprintf("TRACE- pexerr()\n");
    181 #endif
    182 	/* Couldn't find the damn thing */
    183 	if (exerr)
    184 		bferr(exerr);
    185 	bferr("Command not found");
    186 }
    187 
    188 /*
    189  * Execute command f, arg list t.
    190  * Record error message if not found.
    191  * Also do shell scripts here.
    192  */
    193 void
    194 texec(struct command *cmd, tchar *f, tchar **t)
    195 {
    196 	int	pfstatus = 0;
    197 	struct	varent *v;
    198 	tchar	**vp;
    199 	tchar		*lastsh[2];
    200 
    201 #ifdef TRACE
    202 	tprintf("TRACE- texec()\n");
    203 #endif
    204 	/* convert cfname and cargs from tchar to char */
    205 	tconvert(cmd, f, t);
    206 
    207 	if (pfcshflag == 1) {
    208 		pfstatus = secpolicy_pfexec((const char *)(cmd->cfname),
    209 		    cmd->cargs, (const char **)NULL);
    210 		if (pfstatus != NOATTRS) {
    211 			errno = pfstatus;
    212 		}
    213 	}
    214 	if ((pfcshflag == 0) || (pfstatus == NOATTRS)) {
    215 		execv(cmd->cfname, cmd->cargs);
    216 	}
    217 
    218 	/*
    219 	 * exec returned, free up allocations from above
    220 	 * tconvert(), zero cfname and cargs to prevent
    221 	 * duplicate free() in freesyn()
    222 	 */
    223 	xfree(cmd->cfname);
    224 	chr_blkfree(cmd->cargs);
    225 	cmd->cfname = (char *)0;
    226 	cmd->cargs = (char **)0;
    227 
    228 	switch (errno) {
    229 	case ENOEXEC:
    230 		/* check that this is not a binary file */
    231 		{
    232 			int ff = open_(f, 0);
    233 			tchar ch[MB_LEN_MAX];
    234 
    235 			if (ff != -1 && read_(ff, ch, 1) == 1 &&
    236 			    !isprint(ch[0]) && !isspace(ch[0])) {
    237 				printf("Cannot execute binary file.\n");
    238 				Perror(f);
    239 				(void) close(ff);
    240 				unsetfd(ff);
    241 				return;
    242 			}
    243 			(void) close(ff);
    244 			unsetfd(ff);
    245 		}
    246 		/*
    247 		 * If there is an alias for shell, then
    248 		 * put the words of the alias in front of the
    249 		 * argument list replacing the command name.
    250 		 * Note no interpretation of the words at this point.
    251 		 */
    252 		v = adrof1(S_shell /* "shell" */, &aliases);
    253 		if (v == 0) {
    254 #ifdef OTHERSH
    255 			int ff = open_(f, 0);
    256 			tchar ch[MB_LEN_MAX];
    257 #endif
    258 
    259 			vp = lastsh;
    260 			vp[0] = adrof(S_shell /* "shell" */) ? value(S_shell /* "shell" */) : S_SHELLPATH /* SHELLPATH */;
    261 			vp[1] =  (tchar *) NULL;
    262 #ifdef OTHERSH
    263 			if (ff != -1 && read_(ff, ch, 1) == 1 && ch[0] != '#')
    264 				vp[0] = S_OTHERSH /* OTHERSH */;
    265 			(void) close(ff);
    266 			unsetfd(ff);
    267 #endif
    268 		} else
    269 			vp = v->vec;
    270 		t[0] = f;
    271 		t = blkspl(vp, t);		/* Splice up the new arglst */
    272 		f = *t;
    273 
    274 		tconvert(cmd, f, t);		/* convert tchar to char */
    275 
    276 		/*
    277 		 * now done with tchar arg list t,
    278 		 * free the space calloc'd by above blkspl()
    279 		 */
    280 		xfree((char *)t);
    281 
    282 		execv(cmd->cfname, cmd->cargs);	/* exec the command */
    283 
    284 		/* exec returned, same free'ing as above */
    285 		xfree(cmd->cfname);
    286 		chr_blkfree(cmd->cargs);
    287 		cmd->cfname = (char *)0;
    288 		cmd->cargs = (char **)0;
    289 
    290 		/* The sky is falling, the sky is falling! */
    291 
    292 	case ENOMEM:
    293 		Perror(f);
    294 
    295 	case ENOENT:
    296 		break;
    297 
    298 	default:
    299 		if (exerr == 0) {
    300 			exerr = strerror(errno);
    301 			setname(f);
    302 		}
    303 	}
    304 }
    305 
    306 
    307 static void
    308 tconvert(struct command *cmd, tchar *fname, tchar **list)
    309 {
    310 	char **rc;
    311 	int len;
    312 
    313 	cmd->cfname = tstostr(NULL, fname);
    314 
    315 	len = blklen(list);
    316 	rc = cmd->cargs = (char **)
    317 		xcalloc((uint_t)(len + 1), sizeof (char **));
    318 	while (len--)
    319 		*rc++ = tstostr(NULL, *list++);
    320 	*rc = NULL;
    321 }
    322 
    323 
    324 /*ARGSUSED*/
    325 void
    326 execash(tchar **t, struct command *kp)
    327 {
    328 #ifdef TRACE
    329 	tprintf("TRACE- execash()\n");
    330 #endif
    331 
    332 	rechist();
    333 	(void) signal(SIGINT, parintr);
    334 	(void) signal(SIGQUIT, parintr);
    335 	(void) signal(SIGTERM, parterm);	/* if doexec loses, screw */
    336 	lshift(kp->t_dcom, 1);
    337 	exiterr++;
    338 	doexec(kp);
    339 	/*NOTREACHED*/
    340 }
    341 
    342 void
    343 xechoit(tchar **t)
    344 {
    345 #ifdef TRACE
    346 	tprintf("TRACE- xechoit()\n");
    347 #endif
    348 
    349 	if (adrof(S_echo /* "echo" */)) {
    350 		flush();
    351 		haderr = 1;
    352 		blkpr(t), Putchar('\n');
    353 		haderr = 0;
    354 	}
    355 }
    356 
    357 /*
    358  * This routine called when user enters "rehash".
    359  * Both the path and cdpath caching arrays will
    360  * be rehashed, via calling dohash.  If either
    361  * variable is not set with a value, then dohash
    362  * just exits.
    363  */
    364 void
    365 dorehash(void)
    366 {
    367 	dohash(xhash);
    368 	dohash(xhash2);
    369 }
    370 
    371 /*
    372  * Fill up caching arrays for path and cdpath
    373  */
    374 void
    375 dohash(char cachearray[])
    376 {
    377 	struct stat stb;
    378 	DIR *dirp;
    379 	struct dirent *dp;
    380 	int cnt;
    381 	int i = 0;
    382 	struct varent *v;
    383 	tchar **pv;
    384 	int hashval;
    385 	tchar curdir_[MAXNAMLEN+1];
    386 
    387 #ifdef TRACE
    388 	tprintf("TRACE- dohash()\n");
    389 #endif
    390 	/* Caching $path */
    391 	if (cachearray == xhash) {
    392 		havhash = 1;
    393 		v = adrof(S_path /* "path" */);
    394 	} else {    /* Caching $cdpath */
    395 		havhash2 = 1;
    396 		v = adrof(S_cdpath /* "cdpath" */);
    397 	}
    398 
    399 	for (cnt = 0; cnt < (HSHSIZ / 8); cnt++)
    400 		cachearray[cnt] = 0;
    401 	if (v == 0)
    402 		{
    403 		return;
    404 		}
    405 	for (pv = v->vec; *pv; pv++, i++) {
    406 		if (pv[0][0] != '/')
    407 			continue;
    408 		dirp = opendir_(*pv);
    409 		if (dirp == NULL)
    410 			continue;
    411 		if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) {
    412 			unsetfd(dirp->dd_fd);
    413 			closedir_(dirp);
    414 			continue;
    415 		}
    416 		while ((dp = readdir(dirp)) != NULL) {
    417 			if (dp->d_ino == 0)
    418 				continue;
    419 			if (dp->d_name[0] == '.' &&
    420 			    (dp->d_name[1] == '\0' ||
    421 			    dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
    422 				continue;
    423 			hashval = hash(hashname(strtots(curdir_, dp->d_name)), i);
    424 			bis(cachearray, hashval);
    425 		}
    426 		unsetfd(dirp->dd_fd);
    427 		closedir_(dirp);
    428 	}
    429 }
    430 
    431 void
    432 dounhash(void)
    433 {
    434 
    435 #ifdef TRACE
    436 	tprintf("TRACE- dounhash()\n");
    437 #endif
    438 	havhash = 0;
    439 	havhash2 = 0;
    440 }
    441 
    442 #ifdef VFORK
    443 void
    444 hashstat(void)
    445 {
    446 #ifdef TRACE
    447 	tprintf("TRACE- hashstat_()\n");
    448 #endif
    449 
    450 	if (hits+misses)
    451 		printf("%d hits, %d misses, %d%%\n",
    452 			hits, misses, 100 * hits / (hits + misses));
    453 }
    454 #endif
    455 
    456 /*
    457  * Hash a command name.
    458  */
    459 int
    460 hashname(tchar *cp)
    461 {
    462 	long h = 0;
    463 
    464 #ifdef TRACE
    465 	tprintf("TRACE- hashname()\n");
    466 #endif
    467 	while (*cp)
    468 		h = hash(h, *cp++);
    469 	return ((int)h);
    470 }
    471