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