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