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
35static 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 */
62extern 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 */
71static const char *exerr;	/* Execution error message */
72static 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
90static 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 */
97static int hits, misses;
98
99/* Dummy search path for just absolute search when no path */
100static Char *justabs[] = {STRNULL, 0};
101
102static void pexerr(void) __dead;
103static void texec(Char *, Char **);
104static int hashname(Char *);
105static int tellmewhat(struct wordent *, Char *);
106static int executable(Char *, Char *, int);
107static int iscommand(Char *);
108
109void
110/*ARGSUSED*/
111doexec(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++;
232cont:
233	pv++;
234	i++;
235    } while (*pv);
236    hits--;
237    Vsav = 0;
238    free(sav);
239    pexerr();
240    /* NOTREACHED */
241}
242
243static void
244pexerr(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 */
267static void
268texec(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*/
359void
360execash(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
422void
423xechoit(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
437void
438/*ARGSUSED*/
439dohash(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
477void
478/*ARGSUSED*/
479dounhash(Char **v, struct command *t)
480{
481    havhash = 0;
482}
483
484void
485/*ARGSUSED*/
486hashstat(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 */
496static int
497hashname(Char *cp)
498{
499    long h = 0;
500
501    while (*cp)
502	h = hash(h, *cp++);
503    return ((int) h);
504}
505
506static int
507iscommand(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	}
544cont:
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 */
568static int
569executable(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*/
606void
607dowhich(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
637static int
638tellmewhat(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