xref: /illumos-gate/usr/src/cmd/csh/sh.exec.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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