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