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