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 */
38char *exerr;			/* Execution error message */
39
40void	pexerr(void);
41void	texec(struct command *, tchar *, tchar **);
42void	xechoit(tchar **);
43void	dohash(char []);
44
45static void	tconvert(struct command *, tchar *, tchar **);
46
47
48extern DIR *opendir_(tchar *);
49
50void
51doexec(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
157cont:
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
173void
174pexerr(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 */
191void
192texec(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
297static void
298tconvert(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*/
315void
316execash(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
332void
333xechoit(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 */
354void
355dorehash(void)
356{
357	dohash(xhash);
358	dohash(xhash2);
359}
360
361/*
362 * Fill up caching arrays for path and cdpath
363 */
364void
365dohash(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
420void
421dounhash(void)
422{
423
424#ifdef TRACE
425	tprintf("TRACE- dounhash()\n");
426#endif
427	havhash = 0;
428	havhash2 = 0;
429}
430
431#ifdef VFORK
432void
433hashstat(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 */
448int
449hashname(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