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