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