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