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 */
53extern 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 */
62static char *exerr;		/* Execution error message */
63static 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
81static 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 */
86static int hits, misses;
87
88/* Dummy search path for just absolute search when no path */
89static Char *justabs[] = {STRNULL, 0};
90
91static void	pexerr(void);
92static void	texec(Char *, Char **);
93static int	hashname(Char *);
94static int	tellmewhat(struct wordent *, Char *, int len);
95static int	executable(Char *, Char *, bool);
96static int	iscommand(Char *);
97
98
99void
100doexec(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++;
221cont:
222	pv++;
223	i++;
224    } while (*pv);
225    hits--;
226    Vsav = 0;
227    free(sav);
228    pexerr();
229}
230
231static void
232pexerr(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 */
253static void
254texec(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
342void
343execash(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
410void
411xechoit(Char **t)
412{
413    if (adrof(STRecho)) {
414	(void) fflush(csherr);
415	blkpr(csherr, t);
416	(void) fputc('\n', csherr);
417    }
418}
419
420void
421dohash(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
457void
458dounhash(Char **v, struct command *t)
459{
460    havhash = 0;
461}
462
463void
464hashstat(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 */
474static int
475hashname(Char *cp)
476{
477    long h = 0;
478
479    while (*cp)
480	h = hash(h, *cp++);
481    return ((int) h);
482}
483
484static int
485iscommand(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	}
520cont:
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 */
544static int
545executable(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 */
583void
584dowhich(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
614static int
615tellmewhat(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