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
doexec(struct command * t)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
pexerr(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
texec(struct command * cmd,tchar * f,tchar ** t)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
tconvert(struct command * cmd,tchar * fname,tchar ** list)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
execash(tchar ** t,struct command * kp)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
xechoit(tchar ** t)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
dorehash(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
dohash(char cachearray[])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
dounhash(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
hashstat(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
hashname(tchar * cp)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