exec.c revision 1.20 1 /* $NetBSD: exec.c,v 1.20 2003/01/16 09:38:38 kleink Exp $ */
2
3 /*-
4 * Copyright (c) 1980, 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)exec.c 8.3 (Berkeley) 5/23/95";
40 #else
41 __RCSID("$NetBSD: exec.c,v 1.20 2003/01/16 09:38:38 kleink Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48
49 #include <dirent.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56
57 #include "csh.h"
58 #include "extern.h"
59
60 /*
61 * System level search and execute of a command. We look in each directory
62 * for the specified command name. If the name contains a '/' then we
63 * execute only the full path name. If there is no search path then we
64 * execute only full path names.
65 */
66 extern char **environ;
67
68 /*
69 * As we search for the command we note the first non-trivial error
70 * message for presentation to the user. This allows us often
71 * to show that a file has the wrong mode/no access when the file
72 * is not in the last component of the search path, so we must
73 * go on after first detecting the error.
74 */
75 static const char *exerr; /* Execution error message */
76 static Char *expath; /* Path for exerr */
77
78 /*
79 * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
80 * to hash execs. If it is allocated (havhash true), then to tell
81 * whether ``name'' is (possibly) present in the i'th component
82 * of the variable path, you look at the bit in xhash indexed by
83 * hash(hashname("name"), i). This is setup automatically
84 * after .login is executed, and recomputed whenever ``path'' is
85 * changed.
86 * The two part hash function is designed to let texec() call the
87 * more expensive hashname() only once and the simple hash() several
88 * times (once for each path component checked).
89 * Byte size is assumed to be 8.
90 */
91 #define HSHSIZ 8192 /* 1k bytes */
92 #define HSHMASK (HSHSIZ - 1)
93 #define HSHMUL 243
94 static char xhash[HSHSIZ / 8];
95
96 #define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK)
97 #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */
98 #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */
99 static int hits, misses;
100
101 /* Dummy search path for just absolute search when no path */
102 static Char *justabs[] = {STRNULL, 0};
103
104 static void pexerr __P((void)) __attribute__((noreturn));
105 static void texec(Char *, Char **);
106 static int hashname(Char *);
107 static int tellmewhat(struct wordent *, Char *);
108 static int executable(Char *, Char *, bool);
109 static int iscommand(Char *);
110
111 void
112 /*ARGSUSED*/
113 doexec(Char **v, struct command *t)
114 {
115 struct varent *pathv;
116 Char *blk[2], **av, *dp, **pv, *sav;
117 int i, hashval, hashval1;
118 sigset_t nsigset;
119 bool slash;
120
121 hashval = 0;
122 /*
123 * Glob the command name. We will search $path even if this does something,
124 * as in sh but not in csh. One special case: if there is no PATH, then we
125 * execute only commands which start with '/'.
126 */
127 blk[0] = t->t_dcom[0];
128 blk[1] = 0;
129 gflag = 0, tglob(blk);
130 if (gflag) {
131 pv = globall(blk);
132 if (pv == 0) {
133 setname(vis_str(blk[0]));
134 stderror(ERR_NAME | ERR_NOMATCH);
135 }
136 gargv = 0;
137 }
138 else
139 pv = saveblk(blk);
140
141 trim(pv);
142
143 exerr = 0;
144 expath = Strsave(pv[0]);
145 Vexpath = expath;
146
147 pathv = adrof(STRpath);
148 if (pathv == 0 && expath[0] != '/') {
149 blkfree(pv);
150 pexerr();
151 }
152 slash = any(short2str(expath), '/');
153
154 /*
155 * Glob the argument list, if necessary. Otherwise trim off the quote bits.
156 */
157 gflag = 0;
158 av = &t->t_dcom[1];
159 tglob(av);
160 if (gflag) {
161 av = globall(av);
162 if (av == 0) {
163 blkfree(pv);
164 setname(vis_str(expath));
165 stderror(ERR_NAME | ERR_NOMATCH);
166 }
167 gargv = 0;
168 }
169 else
170 av = saveblk(av);
171
172 blkfree(t->t_dcom);
173 t->t_dcom = blkspl(pv, av);
174 xfree((ptr_t) pv);
175 xfree((ptr_t) av);
176 av = t->t_dcom;
177 trim(av);
178
179 if (*av == NULL || **av == '\0')
180 pexerr();
181
182 xechoit(av); /* Echo command if -x */
183 /*
184 * Since all internal file descriptors are set to close on exec, we don't
185 * need to close them explicitly here. Just reorient ourselves for error
186 * messages.
187 */
188 SHIN = 0;
189 SHOUT = 1;
190 SHERR = 2;
191 OLDSTD = 0;
192 /*
193 * We must do this AFTER any possible forking (like `foo` in glob) so that
194 * this shell can still do subprocesses.
195 */
196 sigemptyset(&nsigset);
197 (void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
198 /*
199 * If no path, no words in path, or a / in the filename then restrict the
200 * command search.
201 */
202 if (pathv == 0 || pathv->vec[0] == 0 || slash)
203 pv = justabs;
204 else
205 pv = pathv->vec;
206 sav = Strspl(STRslash, *av); /* / command name for postpending */
207 Vsav = sav;
208 if (havhash)
209 hashval = hashname(*av);
210 i = 0;
211 hits++;
212 do {
213 /*
214 * Try to save time by looking at the hash table for where this command
215 * could be. If we are doing delayed hashing, then we put the names in
216 * one at a time, as the user enters them. This is kinda like Korn
217 * Shell's "tracked aliases".
218 */
219 if (!slash && pv[0][0] == '/' && havhash) {
220 hashval1 = hash(hashval, i);
221 if (!bit(xhash, hashval1))
222 goto cont;
223 }
224 if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */
225 texec(*av, av);
226 else {
227 dp = Strspl(*pv, sav);
228 Vdp = dp;
229 texec(dp, av);
230 Vdp = 0;
231 xfree((ptr_t)dp);
232 }
233 misses++;
234 cont:
235 pv++;
236 i++;
237 } while (*pv);
238 hits--;
239 Vsav = 0;
240 xfree((ptr_t)sav);
241 pexerr();
242 /* NOTREACHED */
243 }
244
245 static void
246 pexerr(void)
247 {
248 /* Couldn't find the damn thing */
249 if (expath) {
250 setname(vis_str(expath));
251 Vexpath = 0;
252 xfree((ptr_t)expath);
253 expath = 0;
254 }
255 else
256 setname("");
257 if (exerr)
258 stderror(ERR_NAME | ERR_STRING, exerr);
259 else
260 stderror(ERR_NAME | ERR_COMMAND);
261 /* NOTREACHED */
262 }
263
264 /*
265 * Execute command f, arg list t.
266 * Record error message if not found.
267 * Also do shell scripts here.
268 */
269 static void
270 texec(Char *sf, Char **st)
271 {
272 struct varent *v;
273 Char *lastsh[2], **vp, *st0, **ost;
274 char *f, **t;
275 int fd;
276 unsigned char c;
277
278 /* The order for the conversions is significant */
279 t = short2blk(st);
280 f = short2str(sf);
281 Vt = t;
282 errno = 0; /* don't use a previous error */
283 (void)execve(f, t, environ);
284 Vt = 0;
285 blkfree((Char **)t);
286 switch (errno) {
287
288 case ENOEXEC:
289 /*
290 * From: casper (at) fwi.uva.nl (Casper H.S. Dik) If we could not execute
291 * it, don't feed it to the shell if it looks like a binary!
292 */
293 if ((fd = open(f, O_RDONLY)) != -1) {
294 if (read(fd, (char *)&c, 1) == 1) {
295 if (!Isprint(c) && (c != '\n' && c != '\t')) {
296 (void)close(fd);
297 /*
298 * We *know* what ENOEXEC means.
299 */
300 stderror(ERR_ARCH, f, strerror(errno));
301 }
302 }
303 #ifdef _PATH_BSHELL
304 else
305 c = '#';
306 #endif
307 (void)close(fd);
308 }
309 /*
310 * If there is an alias for shell, then put the words of the alias in
311 * front of the argument list replacing the command name. Note no
312 * interpretation of the words at this point.
313 */
314 v = adrof1(STRshell, &aliases);
315 if (v == 0) {
316 vp = lastsh;
317 vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
318 vp[1] = NULL;
319 #ifdef _PATH_BSHELL
320 if (fd != -1 && c != '#')
321 vp[0] = STR_BSHELL;
322 #endif
323 }
324 else
325 vp = v->vec;
326 st0 = st[0];
327 st[0] = sf;
328 ost = st;
329 st = blkspl(vp, st); /* Splice up the new arglst */
330 ost[0] = st0;
331 sf = *st;
332 /* The order for the conversions is significant */
333 t = short2blk(st);
334 f = short2str(sf);
335 xfree((ptr_t) st);
336 Vt = t;
337 (void)execve(f, t, environ);
338 Vt = 0;
339 blkfree((Char **) t);
340 /* FALLTHROUGH */
341
342 case ENOMEM:
343 stderror(ERR_SYSTEM, f, strerror(errno));
344 /* NOTREACHED */
345
346 case ENOENT:
347 break;
348
349 default:
350 if (exerr == 0) {
351 exerr = strerror(errno);
352 if (expath)
353 xfree((ptr_t) expath);
354 expath = Strsave(sf);
355 Vexpath = expath;
356 }
357 }
358 }
359
360 /*ARGSUSED*/
361 void
362 execash(Char **t, struct command *kp)
363 {
364 jmp_buf osetexit;
365 sig_t osigint, osigquit, osigterm;
366 int my_reenter, odidfds, oOLDSTD, oSHERR, oSHIN, oSHOUT;
367 int saveDIAG, saveIN, saveOUT, saveSTD;
368
369 if (chkstop == 0 && setintr)
370 panystop(0);
371 /*
372 * Hmm, we don't really want to do that now because we might
373 * fail, but what is the choice
374 */
375 rechist();
376
377 osigint = signal(SIGINT, parintr);
378 osigquit = signal(SIGQUIT, parintr);
379 osigterm = signal(SIGTERM, parterm);
380
381 odidfds = didfds;
382 oSHIN = SHIN;
383 oSHOUT = SHOUT;
384 oSHERR = SHERR;
385 oOLDSTD = OLDSTD;
386
387 saveIN = dcopy(SHIN, -1);
388 saveOUT = dcopy(SHOUT, -1);
389 saveDIAG = dcopy(SHERR, -1);
390 saveSTD = dcopy(OLDSTD, -1);
391
392 lshift(kp->t_dcom, 1);
393
394 getexit(osetexit);
395
396 if ((my_reenter = setexit()) == 0) {
397 SHIN = dcopy(0, -1);
398 SHOUT = dcopy(1, -1);
399 SHERR = dcopy(2, -1);
400 didfds = 0;
401 doexec(t, kp);
402 }
403
404 (void)signal(SIGINT, osigint);
405 (void)signal(SIGQUIT, osigquit);
406 (void)signal(SIGTERM, osigterm);
407
408 doneinp = 0;
409 didfds = odidfds;
410 (void)close(SHIN);
411 (void)close(SHOUT);
412 (void)close(SHERR);
413 (void)close(OLDSTD);
414 SHIN = dmove(saveIN, oSHIN);
415 SHOUT = dmove(saveOUT, oSHOUT);
416 SHERR = dmove(saveDIAG, oSHERR);
417 OLDSTD = dmove(saveSTD, oOLDSTD);
418
419 resexit(osetexit);
420 if (my_reenter)
421 stderror(ERR_SILENT);
422 }
423
424 void
425 xechoit(Char **t)
426 {
427 if (adrof(STRecho)) {
428 int odidfds = didfds;
429 (void)fflush(csherr);
430 odidfds = didfds;
431 didfds = 0;
432 blkpr(csherr, t);
433 (void)fputc('\n', csherr);
434 (void)fflush(csherr);
435 didfds = odidfds;
436 }
437 }
438
439 void
440 /*ARGSUSED*/
441 dohash(Char **v, struct command *t)
442 {
443 struct dirent *dp;
444 struct varent *pathv;
445 DIR *dirp;
446 Char **pv;
447 int cnt, hashval, i;
448
449 i = 0;
450 havhash = 1;
451 pathv = adrof(STRpath);
452
453 for (cnt = 0; cnt < sizeof xhash; cnt++)
454 xhash[cnt] = 0;
455 if (pathv == 0)
456 return;
457 for (pv = pathv->vec; *pv; pv++, i++) {
458 if (pv[0][0] != '/')
459 continue;
460 dirp = opendir(short2str(*pv));
461 if (dirp == NULL)
462 continue;
463 while ((dp = readdir(dirp)) != NULL) {
464 if (dp->d_ino == 0)
465 continue;
466 if (dp->d_name[0] == '.' &&
467 (dp->d_name[1] == '\0' ||
468 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
469 continue;
470 hashval = hash(hashname(str2short(dp->d_name)), i);
471 bis(xhash, hashval);
472 /* tw_add_comm_name (dp->d_name); */
473 }
474 (void) closedir(dirp);
475 }
476 }
477
478 void
479 /*ARGSUSED*/
480 dounhash(Char **v, struct command *t)
481 {
482 havhash = 0;
483 }
484
485 void
486 /*ARGSUSED*/
487 hashstat(Char **v, struct command *t)
488 {
489 if (hits + misses)
490 (void)fprintf(cshout, "%d hits, %d misses, %d%%\n",
491 hits, misses, 100 * hits / (hits + misses));
492 }
493
494 /*
495 * Hash a command name.
496 */
497 static int
498 hashname(Char *cp)
499 {
500 long h = 0;
501
502 while (*cp)
503 h = hash(h, *cp++);
504 return ((int) h);
505 }
506
507 static int
508 iscommand(Char *name)
509 {
510 struct varent *v;
511 Char **pv, *sav;
512 int hashval, hashval1, i;
513 bool slash;
514
515 hashval = 0;
516 slash = any(short2str(name), '/');
517 v = adrof(STRpath);
518
519 if (v == 0 || v->vec[0] == 0 || slash)
520 pv = justabs;
521 else
522 pv = v->vec;
523 sav = Strspl(STRslash, name); /* / command name for postpending */
524 if (havhash)
525 hashval = hashname(name);
526 i = 0;
527 do {
528 if (!slash && pv[0][0] == '/' && havhash) {
529 hashval1 = hash(hashval, i);
530 if (!bit(xhash, hashval1))
531 goto cont;
532 }
533 if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */
534 if (executable(NULL, name, 0)) {
535 xfree((ptr_t) sav);
536 return i + 1;
537 }
538 }
539 else {
540 if (executable(*pv, sav, 0)) {
541 xfree((ptr_t) sav);
542 return i + 1;
543 }
544 }
545 cont:
546 pv++;
547 i++;
548 } while (*pv);
549 xfree((ptr_t) sav);
550 return 0;
551 }
552
553 /* Also by:
554 * Andreas Luik <luik (at) isaak.isa.de>
555 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
556 * Azenberstr. 35
557 * D-7000 Stuttgart 1
558 * West-Germany
559 * is the executable() routine below and changes to iscommand().
560 * Thanks again!!
561 */
562
563 /*
564 * executable() examines the pathname obtained by concatenating dir and name
565 * (dir may be NULL), and returns 1 either if it is executable by us, or
566 * if dir_ok is set and the pathname refers to a directory.
567 * This is a bit kludgy, but in the name of optimization...
568 */
569 static int
570 executable(Char *dir, Char *name, bool dir_ok)
571 {
572 struct stat stbuf;
573 Char path[MAXPATHLEN + 1], *dp, *sp;
574 char *strname;
575
576 if (dir && *dir) {
577 for (dp = path, sp = dir; *sp; *dp++ = *sp++)
578 if (dp == &path[MAXPATHLEN + 1]) {
579 *--dp = '\0';
580 break;
581 }
582 for (sp = name; *sp; *dp++ = *sp++)
583 if (dp == &path[MAXPATHLEN + 1]) {
584 *--dp = '\0';
585 break;
586 }
587 *dp = '\0';
588 strname = short2str(path);
589 }
590 else
591 strname = short2str(name);
592 return (stat(strname, &stbuf) != -1 && ((S_ISREG(stbuf.st_mode) &&
593 /* save time by not calling access() in the hopeless case */
594 (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
595 access(strname, X_OK) == 0) || (dir_ok && S_ISDIR(stbuf.st_mode))));
596 }
597
598 /* The dowhich() is by:
599 * Andreas Luik <luik (at) isaak.isa.de>
600 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
601 * Azenberstr. 35
602 * D-7000 Stuttgart 1
603 * West-Germany
604 * Thanks!!
605 */
606 /*ARGSUSED*/
607 void
608 dowhich(Char **v, struct command *c)
609 {
610 struct wordent lexw[3];
611 struct varent *vp;
612
613 lexw[0].next = &lexw[1];
614 lexw[1].next = &lexw[2];
615 lexw[2].next = &lexw[0];
616
617 lexw[0].prev = &lexw[2];
618 lexw[1].prev = &lexw[0];
619 lexw[2].prev = &lexw[1];
620
621 lexw[0].word = STRNULL;
622 lexw[2].word = STRret;
623
624 while (*++v) {
625 if ((vp = adrof1(*v, &aliases)) != NULL) {
626 (void)fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
627 blkpr(cshout, vp->vec);
628 (void)fputc('\n', cshout);
629 set(STRstatus, Strsave(STR0));
630 }
631 else {
632 lexw[1].word = *v;
633 set(STRstatus, Strsave(tellmewhat(lexw, NULL) ? STR0 : STR1));
634 }
635 }
636 }
637
638 static int
639 tellmewhat(struct wordent *lexp, Char *str)
640 {
641 struct biltins *bptr;
642 struct wordent *sp;
643 Char *cmd, *s0, *s1, *s2;
644 int i;
645 bool aliased, found;
646 Char qc;
647
648 aliased = 0;
649 sp = lexp->next;
650
651 if (adrof1(sp->word, &aliases)) {
652 alias(lexp);
653 sp = lexp->next;
654 aliased = 1;
655 }
656
657 s0 = sp->word; /* to get the memory freeing right... */
658
659 /* handle quoted alias hack */
660 if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
661 (sp->word)++;
662
663 /* do quoting, if it hasn't been done */
664 s1 = s2 = sp->word;
665 while (*s2)
666 switch (*s2) {
667 case '\'':
668 case '"':
669 qc = *s2++;
670 while (*s2 && *s2 != qc)
671 *s1++ = *s2++ | QUOTE;
672 if (*s2)
673 s2++;
674 break;
675 case '\\':
676 if (*++s2)
677 *s1++ = *s2++ | QUOTE;
678 break;
679 default:
680 *s1++ = *s2++;
681 }
682 *s1 = '\0';
683
684 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
685 if (eq(sp->word, str2short(bptr->bname))) {
686 if (str == NULL) {
687 if (aliased)
688 prlex(cshout, lexp);
689 (void)fprintf(cshout, "%s: shell built-in command.\n",
690 vis_str(sp->word));
691 }
692 else
693 (void)Strcpy(str, sp->word);
694 sp->word = s0; /* we save and then restore this */
695 return 1;
696 }
697 }
698
699 sp->word = cmd = globone(sp->word, G_IGNORE);
700
701 if ((i = iscommand(sp->word)) != 0) {
702 Char **pv;
703 struct varent *v;
704 bool slash = any(short2str(sp->word), '/');
705
706 v = adrof(STRpath);
707 if (v == 0 || v->vec[0] == 0 || slash)
708 pv = justabs;
709 else
710 pv = v->vec;
711
712 while (--i)
713 pv++;
714 if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
715 if (!slash) {
716 sp->word = Strspl(STRdotsl, sp->word);
717 prlex(cshout, lexp);
718 xfree((ptr_t) sp->word);
719 }
720 else
721 prlex(cshout, lexp);
722 }
723 else {
724 s1 = Strspl(*pv, STRslash);
725 sp->word = Strspl(s1, sp->word);
726 xfree((ptr_t) s1);
727 if (str == NULL)
728 prlex(cshout, lexp);
729 else
730 (void)Strcpy(str, sp->word);
731 xfree((ptr_t) sp->word);
732 }
733 found = 1;
734 }
735 else {
736 if (str == NULL) {
737 if (aliased)
738 prlex(cshout, lexp);
739 (void)fprintf(csherr,
740 "%s: Command not found.\n", vis_str(sp->word));
741 }
742 else
743 (void)Strcpy(str, sp->word);
744 found = 0;
745 }
746 sp->word = s0; /* we save and then restore this */
747 xfree((ptr_t) cmd);
748 return found;
749 }
750