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