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