]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/csh/exec.c
This commit was generated by cvs2svn to compensate for changes in r33011,
[FreeBSD/FreeBSD.git] / bin / csh / exec.c
1 /*-
2  * Copyright (c) 1980, 1991, 1993
3  *      The Regents of the University of California.  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 #if 0
36 static char sccsid[] = "@(#)exec.c      8.1 (Berkeley) 5/31/93";
37 #else
38 static const char rcsid[] =
39         "$Id: exec.c,v 1.7 1997/08/07 21:42:07 steve Exp $";
40 #endif
41 #endif /* not lint */
42
43 #include <sys/param.h>
44 #include <dirent.h>
45 #include <fcntl.h>
46 #include <sys/stat.h>
47 #include <errno.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #if __STDC__
52 # include <stdarg.h>
53 #else
54 # include <varargs.h>
55 #endif
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 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));
105 static void     texec __P((Char *, Char **));
106 static int      hashname __P((Char *));
107 static void     tellmewhat __P((struct wordent *));
108 static int      executable __P((Char *, Char *, bool));
109 static int      iscommand __P((Char *));
110
111
112 void
113 /*ARGSUSED*/
114 doexec(v, t)
115     Char **v;
116     struct command *t;
117 {
118     Char *dp, **pv, **av, *sav;
119     struct varent *pathv;
120     bool slash;
121     int hashval = 0, hashval1, i;
122     Char   *blk[2];
123
124     /*
125      * Glob the command name. We will search $path even if this does something,
126      * as in sh but not in csh.  One special case: if there is no PATH, then we
127      * execute only commands which start with '/'.
128      */
129     blk[0] = t->t_dcom[0];
130     blk[1] = 0;
131     gflag = 0, tglob(blk);
132     if (gflag) {
133         pv = globall(blk);
134         if (pv == 0) {
135             setname(vis_str(blk[0]));
136             stderror(ERR_NAME | ERR_NOMATCH);
137         }
138         gargv = 0;
139     }
140     else
141         pv = saveblk(blk);
142
143     trim(pv);
144
145     exerr = 0;
146     expath = Strsave(pv[0]);
147     Vexpath = expath;
148
149     pathv = adrof(STRpath);
150     if (pathv == 0 && expath[0] != '/') {
151         blkfree(pv);
152         pexerr();
153     }
154     slash = any(short2str(expath), '/');
155
156     /*
157      * Glob the argument list, if necessary. Otherwise trim off the quote bits.
158      */
159     gflag = 0;
160     av = &t->t_dcom[1];
161     tglob(av);
162     if (gflag) {
163         av = globall(av);
164         if (av == 0) {
165             blkfree(pv);
166             setname(vis_str(expath));
167             stderror(ERR_NAME | ERR_NOMATCH);
168         }
169         gargv = 0;
170     }
171     else
172         av = saveblk(av);
173
174     blkfree(t->t_dcom);
175     t->t_dcom = blkspl(pv, av);
176     xfree((ptr_t) pv);
177     xfree((ptr_t) av);
178     av = t->t_dcom;
179     trim(av);
180
181     if (*av == NULL || **av == '\0')
182         pexerr();
183
184     xechoit(av);                /* Echo command if -x */
185     /*
186      * Since all internal file descriptors are set to close on exec, we don't
187      * need to close them explicitly here.  Just reorient ourselves for error
188      * messages.
189      */
190     SHIN = 0;
191     SHOUT = 1;
192     SHERR = 2;
193     OLDSTD = 0;
194     /*
195      * We must do this AFTER any possible forking (like `foo` in glob) so that
196      * this shell can still do subprocesses.
197      */
198     (void) sigsetmask((sigset_t) 0);
199     /*
200      * If no path, no words in path, or a / in the filename then restrict the
201      * command search.
202      */
203     if (pathv == 0 || pathv->vec[0] == 0 || slash)
204         pv = justabs;
205     else
206         pv = pathv->vec;
207     sav = Strspl(STRslash, *av);/* / command name for postpending */
208     Vsav = sav;
209     if (havhash)
210         hashval = hashname(*av);
211     i = 0;
212     hits++;
213     do {
214         /*
215          * Try to save time by looking at the hash table for where this command
216          * could be.  If we are doing delayed hashing, then we put the names in
217          * one at a time, as the user enters them.  This is kinda like Korn
218          * Shell's "tracked aliases".
219          */
220         if (!slash && pv[0][0] == '/' && havhash) {
221             hashval1 = hash(hashval, i);
222             if (!bit(xhash, hashval1))
223                 goto cont;
224         }
225         if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */
226             texec(*av, av);
227         else {
228             dp = Strspl(*pv, sav);
229             Vdp = dp;
230             texec(dp, av);
231             Vdp = 0;
232             xfree((ptr_t) dp);
233         }
234         misses++;
235 cont:
236         pv++;
237         i++;
238     } while (*pv);
239     hits--;
240     Vsav = 0;
241     xfree((ptr_t) sav);
242     pexerr();
243 }
244
245 static void
246 pexerr()
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     stderror(ERR_NAME | ERR_COMMAND);
260 }
261
262 /*
263  * Execute command f, arg list t.
264  * Record error message if not found.
265  * Also do shell scripts here.
266  */
267 static void
268 texec(sf, st)
269     Char   *sf;
270     Char **st;
271 {
272     char **t;
273     char *f;
274     struct varent *v;
275     Char **vp;
276     Char   *lastsh[2];
277     int     fd;
278     unsigned char c;
279     Char   *st0, **ost;
280
281     /* The order for the conversions is significant */
282     t = short2blk(st);
283     f = short2str(sf);
284     Vt = t;
285     errno = 0;                  /* don't use a previous error */
286     (void) execve(f, t, environ);
287     Vt = 0;
288     blkfree((Char **) t);
289     switch (errno) {
290
291     case ENOEXEC:
292         /*
293          * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
294          * it, don't feed it to the shell if it looks like a binary!
295          */
296         if ((fd = open(f, O_RDONLY)) != -1) {
297             if (read(fd, (char *) &c, 1) == 1) {
298                 if (!Isprint(c) && (c != '\n' && c != '\t')) {
299                     (void) close(fd);
300                     /*
301                      * We *know* what ENOEXEC means.
302                      */
303                     stderror(ERR_ARCH, f, strerror(errno));
304                 }
305             }
306 #ifdef _PATH_BSHELL
307             else
308                 c = '#';
309 #endif
310             (void) close(fd);
311         }
312         /*
313          * If there is an alias for shell, then put the words of the alias in
314          * front of the argument list replacing the command name. Note no
315          * interpretation of the words at this point.
316          */
317         v = adrof1(STRshell, &aliases);
318         if (v == 0) {
319             vp = lastsh;
320             vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
321             vp[1] = NULL;
322 #ifdef _PATH_BSHELL
323             if (fd != -1 && c != '#')
324                 vp[0] = STR_BSHELL;
325 #endif
326         }
327         else
328             vp = v->vec;
329         st0 = st[0];
330         st[0] = sf;
331         ost = st;
332         st = blkspl(vp, st);    /* Splice up the new arglst */
333         ost[0] = st0;
334         sf = *st;
335         /* The order for the conversions is significant */
336         t = short2blk(st);
337         f = short2str(sf);
338         xfree((ptr_t) st);
339         Vt = t;
340         (void) execve(f, t, environ);
341         Vt = 0;
342         blkfree((Char **) t);
343         /* The sky is falling, the sky is falling! */
344
345     case ENOMEM:
346         stderror(ERR_SYSTEM, f, strerror(errno));
347
348     case ENOENT:
349         break;
350
351     default:
352         if (exerr == 0) {
353             exerr = strerror(errno);
354             if (expath)
355                 xfree((ptr_t) expath);
356             expath = Strsave(sf);
357             Vexpath = expath;
358         }
359     }
360 }
361
362 /*ARGSUSED*/
363 void
364 execash(t, kp)
365     Char  **t;
366     struct command *kp;
367 {
368     int     saveIN, saveOUT, saveDIAG, saveSTD;
369     int     oSHIN;
370     int     oSHOUT;
371     int     oSHERR;
372     int     oOLDSTD;
373     jmp_buf osetexit;
374     int     my_reenter;
375     int     odidfds;
376     sig_t   osigint, osigquit, osigterm;
377
378     if (chkstop == 0 && setintr)
379         panystop(0);
380     /*
381      * Hmm, we don't really want to do that now because we might
382      * fail, but what is the choice
383      */
384     rechist();
385
386     osigint  = signal(SIGINT, parintr);
387     osigquit = signal(SIGQUIT, parintr);
388     osigterm = signal(SIGTERM, parterm);
389
390     odidfds = didfds;
391     oSHIN = SHIN;
392     oSHOUT = SHOUT;
393     oSHERR = SHERR;
394     oOLDSTD = OLDSTD;
395
396     saveIN = dcopy(SHIN, -1);
397     saveOUT = dcopy(SHOUT, -1);
398     saveDIAG = dcopy(SHERR, -1);
399     saveSTD = dcopy(OLDSTD, -1);
400
401     lshift(kp->t_dcom, 1);
402
403     getexit(osetexit);
404
405     if ((my_reenter = setexit()) == 0) {
406         SHIN = dcopy(0, -1);
407         SHOUT = dcopy(1, -1);
408         SHERR = dcopy(2, -1);
409         didfds = 0;
410         doexec(t, kp);
411     }
412
413     (void) signal(SIGINT, osigint);
414     (void) signal(SIGQUIT, osigquit);
415     (void) signal(SIGTERM, osigterm);
416
417     doneinp = 0;
418     didfds = odidfds;
419     (void) close(SHIN);
420     (void) close(SHOUT);
421     (void) close(SHERR);
422     (void) close(OLDSTD);
423     SHIN = dmove(saveIN, oSHIN);
424     SHOUT = dmove(saveOUT, oSHOUT);
425     SHERR = dmove(saveDIAG, oSHERR);
426     OLDSTD = dmove(saveSTD, oOLDSTD);
427
428     resexit(osetexit);
429     if (my_reenter)
430         stderror(ERR_SILENT);
431 }
432
433 void
434 xechoit(t)
435     Char  **t;
436 {
437     if (adrof(STRecho)) {
438         (void) fflush(csherr);
439         blkpr(csherr, t);
440         (void) fputc('\n', csherr);
441     }
442 }
443
444 void
445 /*ARGSUSED*/
446 dohash(v, t)
447     Char **v;
448     struct command *t;
449 {
450     DIR    *dirp;
451     struct dirent *dp;
452     int cnt;
453     int     i = 0;
454     struct varent *pathv = adrof(STRpath);
455     Char  **pv;
456     int     hashval;
457
458     havhash = 1;
459     for (cnt = 0; cnt < sizeof xhash; cnt++)
460         xhash[cnt] = 0;
461     if (pathv == 0)
462         return;
463     for (pv = pathv->vec; *pv; pv++, i++) {
464         if (pv[0][0] != '/')
465             continue;
466         dirp = opendir(short2str(*pv));
467         if (dirp == NULL)
468             continue;
469         while ((dp = readdir(dirp)) != NULL) {
470             if (dp->d_ino == 0)
471                 continue;
472             if (dp->d_name[0] == '.' &&
473                 (dp->d_name[1] == '\0' ||
474                  (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
475                 continue;
476             hashval = hash(hashname(str2short(dp->d_name)), i);
477             bis(xhash, hashval);
478             /* tw_add_comm_name (dp->d_name); */
479         }
480         (void) closedir(dirp);
481     }
482 }
483
484 void
485 /*ARGSUSED*/
486 dounhash(v, t)
487     Char **v;
488     struct command *t;
489 {
490     havhash = 0;
491 }
492
493 void
494 /*ARGSUSED*/
495 hashstat(v, t)
496     Char **v;
497     struct command *t;
498 {
499     if (hits + misses)
500         (void) fprintf(cshout, "%d hits, %d misses, %d%%\n",
501                        hits, misses, 100 * hits / (hits + misses));
502 }
503
504 /*
505  * Hash a command name.
506  */
507 static int
508 hashname(cp)
509     Char *cp;
510 {
511     long h = 0;
512
513     while (*cp)
514         h = hash(h, *cp++);
515     return ((int) h);
516 }
517
518 static int
519 iscommand(name)
520     Char   *name;
521 {
522     Char **pv;
523     Char *sav;
524     struct varent *v;
525     bool slash = any(short2str(name), '/');
526     int hashval = 0, hashval1, i;
527
528     v = adrof(STRpath);
529     if (v == 0 || v->vec[0] == 0 || slash)
530         pv = justabs;
531     else
532         pv = v->vec;
533     sav = Strspl(STRslash, name);       /* / command name for postpending */
534     if (havhash)
535         hashval = hashname(name);
536     i = 0;
537     do {
538         if (!slash && pv[0][0] == '/' && havhash) {
539             hashval1 = hash(hashval, i);
540             if (!bit(xhash, hashval1))
541                 goto cont;
542         }
543         if (pv[0][0] == 0 || eq(pv[0], STRdot)) {       /* don't make ./xxx */
544             if (executable(NULL, name, 0)) {
545                 xfree((ptr_t) sav);
546                 return i + 1;
547             }
548         }
549         else {
550             if (executable(*pv, sav, 0)) {
551                 xfree((ptr_t) sav);
552                 return i + 1;
553             }
554         }
555 cont:
556         pv++;
557         i++;
558     } while (*pv);
559     xfree((ptr_t) sav);
560     return 0;
561 }
562
563 /* Also by:
564  *  Andreas Luik <luik@isaak.isa.de>
565  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
566  *  Azenberstr. 35
567  *  D-7000 Stuttgart 1
568  *  West-Germany
569  * is the executable() routine below and changes to iscommand().
570  * Thanks again!!
571  */
572
573 /*
574  * executable() examines the pathname obtained by concatenating dir and name
575  * (dir may be NULL), and returns 1 either if it is executable by us, or
576  * if dir_ok is set and the pathname refers to a directory.
577  * This is a bit kludgy, but in the name of optimization...
578  */
579 static int
580 executable(dir, name, dir_ok)
581     Char   *dir, *name;
582     bool    dir_ok;
583 {
584     struct stat stbuf;
585     Char    path[MAXPATHLEN + 1], *dp, *sp;
586     char   *strname;
587
588     if (dir && *dir) {
589         for (dp = path, sp = dir; *sp; *dp++ = *sp++)
590             if (dp == &path[MAXPATHLEN + 1]) {
591                 *--dp = '\0';
592                 break;
593             }
594         for (sp = name; *sp; *dp++ = *sp++)
595             if (dp == &path[MAXPATHLEN + 1]) {
596                 *--dp = '\0';
597                 break;
598             }
599         *dp = '\0';
600         strname = short2str(path);
601     }
602     else
603         strname = short2str(name);
604     return (stat(strname, &stbuf) != -1 &&
605             ((S_ISREG(stbuf.st_mode) &&
606     /* save time by not calling access() in the hopeless case */
607               (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
608               access(strname, X_OK) == 0) ||
609              (dir_ok && S_ISDIR(stbuf.st_mode))));
610 }
611
612 /* The dowhich() is by:
613  *  Andreas Luik <luik@isaak.isa.de>
614  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
615  *  Azenberstr. 35
616  *  D-7000 Stuttgart 1
617  *  West-Germany
618  * Thanks!!
619  */
620 /*ARGSUSED*/
621 void
622 dowhich(v, c)
623     Char **v;
624     struct command *c;
625 {
626     struct wordent lex[3];
627     struct varent *vp;
628
629     lex[0].next = &lex[1];
630     lex[1].next = &lex[2];
631     lex[2].next = &lex[0];
632
633     lex[0].prev = &lex[2];
634     lex[1].prev = &lex[0];
635     lex[2].prev = &lex[1];
636
637     lex[0].word = STRNULL;
638     lex[2].word = STRret;
639
640     while (*++v) {
641         if ((vp = adrof1(*v, &aliases)) != NULL) {
642             (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
643             blkpr(cshout, vp->vec);
644             (void) fputc('\n', cshout);
645         }
646         else {
647             lex[1].word = *v;
648             tellmewhat(lex);
649         }
650     }
651 }
652
653 static void
654 tellmewhat(lex)
655     struct wordent *lex;
656 {
657     int i;
658     struct biltins *bptr;
659     struct wordent *sp = lex->next;
660     bool    aliased = 0;
661     Char   *s0, *s1, *s2, *cmd;
662     Char    qc;
663
664     if (adrof1(sp->word, &aliases)) {
665         alias(lex);
666         sp = lex->next;
667         aliased = 1;
668     }
669
670     s0 = sp->word;              /* to get the memory freeing right... */
671
672     /* handle quoted alias hack */
673     if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
674         (sp->word)++;
675
676     /* do quoting, if it hasn't been done */
677     s1 = s2 = sp->word;
678     while (*s2)
679         switch (*s2) {
680         case '\'':
681         case '"':
682             qc = *s2++;
683             while (*s2 && *s2 != qc)
684                 *s1++ = *s2++ | QUOTE;
685             if (*s2)
686                 s2++;
687             break;
688         case '\\':
689             if (*++s2)
690                 *s1++ = *s2++ | QUOTE;
691             break;
692         default:
693             *s1++ = *s2++;
694         }
695     *s1 = '\0';
696
697     for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
698         if (eq(sp->word, str2short(bptr->bname))) {
699             if (aliased)
700                 prlex(cshout, lex);
701             (void) fprintf(cshout, "%s: shell built-in command.\n",
702                            vis_str(sp->word));
703             sp->word = s0;      /* we save and then restore this */
704             return;
705         }
706     }
707
708     sp->word = cmd = globone(sp->word, G_IGNORE);
709
710     if ((i = iscommand(strip(sp->word))) != 0) {
711         Char **pv;
712         struct varent *v;
713         bool    slash = any(short2str(sp->word), '/');
714
715         v = adrof(STRpath);
716         if (v == 0 || v->vec[0] == 0 || slash)
717             pv = justabs;
718         else
719             pv = v->vec;
720
721         while (--i)
722             pv++;
723         if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
724             if (!slash) {
725                 sp->word = Strspl(STRdotsl, sp->word);
726                 prlex(cshout, lex);
727                 xfree((ptr_t) sp->word);
728             } else
729                 prlex(cshout, lex);
730             sp->word = s0;      /* we save and then restore this */
731             xfree((ptr_t) cmd);
732             return;
733         }
734         s1 = Strspl(*pv, STRslash);
735         sp->word = Strspl(s1, sp->word);
736         xfree((ptr_t) s1);
737         prlex(cshout, lex);
738         xfree((ptr_t) sp->word);
739     }
740     else {
741         if (aliased)
742             prlex(cshout, lex);
743         (void) fprintf(csherr, "%s: Command not found.\n", vis_str(sp->word));
744     }
745     xfree((ptr_t) cmd);
746     sp->word = s0;              /* we save and then restore this */
747 }