Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /*- |
2 | * Copyright (c) 1980, 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | static char sccsid[] = "@(#)exec.c 5.17 (Berkeley) 6/17/91"; | |
36 | #endif /* not lint */ | |
37 | ||
38 | #include <sys/types.h> | |
39 | #include <dirent.h> | |
40 | #include <fcntl.h> | |
41 | #include <errno.h> | |
42 | #include <stdlib.h> | |
43 | #include <string.h> | |
44 | #include <unistd.h> | |
45 | #if __STDC__ | |
46 | # include <stdarg.h> | |
47 | #else | |
48 | # include <varargs.h> | |
49 | #endif | |
50 | ||
51 | #include "csh.h" | |
52 | #include "extern.h" | |
53 | ||
54 | /* | |
55 | * System level search and execute of a command. We look in each directory | |
56 | * for the specified command name. If the name contains a '/' then we | |
57 | * execute only the full path name. If there is no search path then we | |
58 | * execute only full path names. | |
59 | */ | |
60 | extern char **environ; | |
61 | ||
62 | /* | |
63 | * As we search for the command we note the first non-trivial error | |
64 | * message for presentation to the user. This allows us often | |
65 | * to show that a file has the wrong mode/no access when the file | |
66 | * is not in the last component of the search path, so we must | |
67 | * go on after first detecting the error. | |
68 | */ | |
69 | static char *exerr; /* Execution error message */ | |
70 | static Char *expath; /* Path for exerr */ | |
71 | ||
72 | /* | |
73 | * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used | |
74 | * to hash execs. If it is allocated (havhash true), then to tell | |
75 | * whether ``name'' is (possibly) present in the i'th component | |
76 | * of the variable path, you look at the bit in xhash indexed by | |
77 | * hash(hashname("name"), i). This is setup automatically | |
78 | * after .login is executed, and recomputed whenever ``path'' is | |
79 | * changed. | |
80 | * The two part hash function is designed to let texec() call the | |
81 | * more expensive hashname() only once and the simple hash() several | |
82 | * times (once for each path component checked). | |
83 | * Byte size is assumed to be 8. | |
84 | */ | |
85 | #define HSHSIZ 8192 /* 1k bytes */ | |
86 | #define HSHMASK (HSHSIZ - 1) | |
87 | #define HSHMUL 243 | |
88 | static char xhash[HSHSIZ / 8]; | |
89 | ||
90 | #define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK) | |
91 | #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ | |
92 | #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ | |
93 | static int hits, misses; | |
94 | ||
95 | /* Dummy search path for just absolute search when no path */ | |
96 | static Char *justabs[] = {STRNULL, 0}; | |
97 | ||
98 | static void pexerr __P((void)); | |
99 | static void texec __P((Char *, Char **)); | |
100 | static int hashname __P((Char *)); | |
101 | ||
102 | void | |
103 | doexec(t) | |
104 | register struct command *t; | |
105 | { | |
106 | register Char *dp, **pv, **av, *sav; | |
107 | register struct varent *v; | |
108 | register bool slash; | |
109 | register int hashval = 0, hashval1, i; | |
110 | Char *blk[2]; | |
111 | ||
112 | /* | |
113 | * Glob the command name. We will search $path even if this does something, | |
114 | * as in sh but not in csh. One special case: if there is no PATH, then we | |
115 | * execute only commands which start with '/'. | |
116 | */ | |
117 | blk[0] = t->t_dcom[0]; | |
118 | blk[1] = 0; | |
119 | gflag = 0, tglob(blk); | |
120 | if (gflag) { | |
121 | pv = globall(blk); | |
122 | if (pv == 0) { | |
123 | setname(short2str(blk[0])); | |
124 | stderror(ERR_NAME | ERR_NOMATCH); | |
125 | } | |
126 | gargv = 0; | |
127 | } | |
128 | else | |
129 | pv = saveblk(blk); | |
130 | ||
131 | trim(pv); | |
132 | ||
133 | exerr = 0; | |
134 | expath = Strsave(pv[0]); | |
135 | Vexpath = expath; | |
136 | ||
137 | v = adrof(STRpath); | |
138 | if (v == 0 && expath[0] != '/') { | |
139 | blkfree(pv); | |
140 | pexerr(); | |
141 | } | |
142 | slash = any(short2str(expath), '/'); | |
143 | ||
144 | /* | |
145 | * Glob the argument list, if necessary. Otherwise trim off the quote bits. | |
146 | */ | |
147 | gflag = 0; | |
148 | av = &t->t_dcom[1]; | |
149 | tglob(av); | |
150 | if (gflag) { | |
151 | av = globall(av); | |
152 | if (av == 0) { | |
153 | blkfree(pv); | |
154 | setname(short2str(expath)); | |
155 | stderror(ERR_NAME | ERR_NOMATCH); | |
156 | } | |
157 | gargv = 0; | |
158 | } | |
159 | else | |
160 | av = saveblk(av); | |
161 | ||
162 | blkfree(t->t_dcom); | |
163 | t->t_dcom = blkspl(pv, av); | |
164 | xfree((ptr_t) pv); | |
165 | xfree((ptr_t) av); | |
166 | av = t->t_dcom; | |
167 | trim(av); | |
168 | ||
169 | if (*av == NULL || **av == '\0') | |
170 | pexerr(); | |
171 | ||
172 | xechoit(av); /* Echo command if -x */ | |
173 | /* | |
174 | * Since all internal file descriptors are set to close on exec, we don't | |
175 | * need to close them explicitly here. Just reorient ourselves for error | |
176 | * messages. | |
177 | */ | |
178 | SHIN = 0; | |
179 | SHOUT = 1; | |
180 | SHDIAG = 2; | |
181 | OLDSTD = 0; | |
182 | /* | |
183 | * We must do this AFTER any possible forking (like `foo` in glob) so that | |
184 | * this shell can still do subprocesses. | |
185 | */ | |
186 | (void) sigsetmask((sigset_t) 0); | |
187 | /* | |
188 | * If no path, no words in path, or a / in the filename then restrict the | |
189 | * command search. | |
190 | */ | |
191 | if (v == 0 || v->vec[0] == 0 || slash) | |
192 | pv = justabs; | |
193 | else | |
194 | pv = v->vec; | |
195 | sav = Strspl(STRslash, *av);/* / command name for postpending */ | |
196 | Vsav = sav; | |
197 | if (havhash) | |
198 | hashval = hashname(*av); | |
199 | i = 0; | |
200 | hits++; | |
201 | do { | |
202 | /* | |
203 | * Try to save time by looking at the hash table for where this command | |
204 | * could be. If we are doing delayed hashing, then we put the names in | |
205 | * one at a time, as the user enters them. This is kinda like Korn | |
206 | * Shell's "tracked aliases". | |
207 | */ | |
208 | if (!slash && pv[0][0] == '/' && havhash) { | |
209 | hashval1 = hash(hashval, i); | |
210 | if (!bit(xhash, hashval1)) | |
211 | goto cont; | |
212 | } | |
213 | if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ | |
214 | texec(*av, av); | |
215 | else { | |
216 | dp = Strspl(*pv, sav); | |
217 | Vdp = dp; | |
218 | texec(dp, av); | |
219 | Vdp = 0; | |
220 | xfree((ptr_t) dp); | |
221 | } | |
222 | misses++; | |
223 | cont: | |
224 | pv++; | |
225 | i++; | |
226 | } while (*pv); | |
227 | hits--; | |
228 | Vsav = 0; | |
229 | xfree((ptr_t) sav); | |
230 | pexerr(); | |
231 | } | |
232 | ||
233 | static void | |
234 | pexerr() | |
235 | { | |
236 | /* Couldn't find the damn thing */ | |
237 | if (expath) { | |
238 | setname(short2str(expath)); | |
239 | Vexpath = 0; | |
240 | xfree((ptr_t) expath); | |
241 | expath = 0; | |
242 | } | |
243 | else | |
244 | setname(""); | |
245 | if (exerr) | |
246 | stderror(ERR_NAME | ERR_STRING, exerr); | |
247 | stderror(ERR_NAME | ERR_COMMAND); | |
248 | } | |
249 | ||
250 | /* | |
251 | * Execute command f, arg list t. | |
252 | * Record error message if not found. | |
253 | * Also do shell scripts here. | |
254 | */ | |
255 | static void | |
256 | texec(sf, st) | |
257 | Char *sf; | |
258 | register Char **st; | |
259 | { | |
260 | register char **t; | |
261 | register char *f; | |
262 | register struct varent *v; | |
263 | register Char **vp; | |
264 | Char *lastsh[2]; | |
265 | int fd; | |
266 | unsigned char c; | |
267 | Char *st0, **ost; | |
268 | ||
269 | /* The order for the conversions is significant */ | |
270 | t = short2blk(st); | |
271 | f = short2str(sf); | |
272 | Vt = t; | |
273 | errno = 0; /* don't use a previous error */ | |
274 | (void) execve(f, t, environ); | |
275 | Vt = 0; | |
276 | blkfree((Char **) t); | |
277 | switch (errno) { | |
278 | ||
279 | case ENOEXEC: | |
280 | /* | |
281 | * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute | |
282 | * it, don't feed it to the shell if it looks like a binary! | |
283 | */ | |
284 | if ((fd = open(f, O_RDONLY)) != -1) { | |
285 | if (read(fd, (char *) &c, 1) == 1) { | |
286 | if (!Isprint(c) && (c != '\n' && c != '\t')) { | |
287 | (void) close(fd); | |
288 | /* | |
289 | * We *know* what ENOEXEC means. | |
290 | */ | |
291 | stderror(ERR_ARCH, f, strerror(errno)); | |
292 | } | |
293 | } | |
294 | #ifdef _PATH_BSHELL | |
295 | else | |
296 | c = '#'; | |
297 | #endif | |
298 | (void) close(fd); | |
299 | } | |
300 | /* | |
301 | * If there is an alias for shell, then put the words of the alias in | |
302 | * front of the argument list replacing the command name. Note no | |
303 | * interpretation of the words at this point. | |
304 | */ | |
305 | v = adrof1(STRshell, &aliases); | |
306 | if (v == 0) { | |
307 | vp = lastsh; | |
308 | vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; | |
309 | vp[1] = NULL; | |
310 | #ifdef _PATH_BSHELL | |
311 | if (fd != -1 && c != '#') | |
312 | vp[0] = STR_BSHELL; | |
313 | #endif | |
314 | } | |
315 | else | |
316 | vp = v->vec; | |
317 | st0 = st[0]; | |
318 | st[0] = sf; | |
319 | ost = st; | |
320 | st = blkspl(vp, st); /* Splice up the new arglst */ | |
321 | ost[0] = st0; | |
322 | sf = *st; | |
323 | /* The order for the conversions is significant */ | |
324 | t = short2blk(st); | |
325 | f = short2str(sf); | |
326 | xfree((ptr_t) st); | |
327 | Vt = t; | |
328 | (void) execve(f, t, environ); | |
329 | Vt = 0; | |
330 | blkfree((Char **) t); | |
331 | /* The sky is falling, the sky is falling! */ | |
332 | ||
333 | case ENOMEM: | |
334 | stderror(ERR_SYSTEM, f, strerror(errno)); | |
335 | ||
336 | case ENOENT: | |
337 | break; | |
338 | ||
339 | default: | |
340 | if (exerr == 0) { | |
341 | exerr = strerror(errno); | |
342 | if (expath) | |
343 | xfree((ptr_t) expath); | |
344 | expath = Strsave(sf); | |
345 | Vexpath = expath; | |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | /*ARGSUSED*/ | |
351 | void | |
352 | execash(t, kp) | |
353 | char **t; | |
354 | register struct command *kp; | |
355 | { | |
356 | if (chkstop == 0 && setintr) | |
357 | panystop(0); | |
358 | rechist(); | |
359 | (void) signal(SIGINT, parintr); | |
360 | (void) signal(SIGQUIT, parintr); | |
361 | (void) signal(SIGTERM, parterm); /* if doexec loses, screw */ | |
362 | lshift(kp->t_dcom, 1); | |
363 | exiterr = 1; | |
364 | doexec(kp); | |
365 | /* NOTREACHED */ | |
366 | } | |
367 | ||
368 | void | |
369 | xechoit(t) | |
370 | Char **t; | |
371 | { | |
372 | if (adrof(STRecho)) { | |
373 | flush(); | |
374 | haderr = 1; | |
375 | blkpr(t), xputchar('\n'); | |
376 | haderr = 0; | |
377 | } | |
378 | } | |
379 | ||
380 | /*VARARGS0*/ | |
381 | void | |
382 | dohash() | |
383 | { | |
384 | DIR *dirp; | |
385 | register struct dirent *dp; | |
386 | register int cnt; | |
387 | int i = 0; | |
388 | struct varent *v = adrof(STRpath); | |
389 | Char **pv; | |
390 | int hashval; | |
391 | ||
392 | havhash = 1; | |
393 | for (cnt = 0; cnt < sizeof xhash; cnt++) | |
394 | xhash[cnt] = 0; | |
395 | if (v == 0) | |
396 | return; | |
397 | for (pv = v->vec; *pv; pv++, i++) { | |
398 | if (pv[0][0] != '/') | |
399 | continue; | |
400 | dirp = opendir(short2str(*pv)); | |
401 | if (dirp == NULL) | |
402 | continue; | |
403 | while ((dp = readdir(dirp)) != NULL) { | |
404 | if (dp->d_ino == 0) | |
405 | continue; | |
406 | if (dp->d_name[0] == '.' && | |
407 | (dp->d_name[1] == '\0' || | |
408 | dp->d_name[1] == '.' && dp->d_name[2] == '\0')) | |
409 | continue; | |
410 | hashval = hash(hashname(str2short(dp->d_name)), i); | |
411 | bis(xhash, hashval); | |
412 | /* tw_add_comm_name (dp->d_name); */ | |
413 | } | |
414 | (void) closedir(dirp); | |
415 | } | |
416 | } | |
417 | ||
418 | void | |
419 | dounhash() | |
420 | { | |
421 | havhash = 0; | |
422 | } | |
423 | ||
424 | void | |
425 | hashstat() | |
426 | { | |
427 | if (hits + misses) | |
428 | xprintf("%d hits, %d misses, %d%%\n", | |
429 | hits, misses, 100 * hits / (hits + misses)); | |
430 | } | |
431 | ||
432 | /* | |
433 | * Hash a command name. | |
434 | */ | |
435 | static int | |
436 | hashname(cp) | |
437 | register Char *cp; | |
438 | { | |
439 | register long h = 0; | |
440 | ||
441 | while (*cp) | |
442 | h = hash(h, *cp++); | |
443 | return ((int) h); | |
444 | } |