1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
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 <sys/stat.h>
35#include <unistd.h>
36#include <fcntl.h>
37#include <errno.h>
38#include <paths.h>
39#include <stdbool.h>
40#include <stdlib.h>
41
42/*
43 * When commands are first encountered, they are entered in a hash table.
44 * This ensures that a full path search will not have to be done for them
45 * on each invocation.
46 *
47 * We should investigate converting to a linear search, even though that
48 * would make the command name "hash" a misnomer.
49 */
50
51#include "shell.h"
52#include "main.h"
53#include "nodes.h"
54#include "parser.h"
55#include "redir.h"
56#include "eval.h"
57#include "exec.h"
58#include "builtins.h"
59#include "var.h"
60#include "options.h"
61#include "input.h"
62#include "output.h"
63#include "syntax.h"
64#include "memalloc.h"
65#include "error.h"
66#include "mystring.h"
67#include "show.h"
68#include "jobs.h"
69#include "alias.h"
70
71
72#define CMDTABLESIZE 31		/* should be prime */
73
74
75
76struct tblentry {
77	struct tblentry *next;	/* next entry in hash chain */
78	union param param;	/* definition of builtin function */
79	int special;		/* flag for special builtin commands */
80	signed char cmdtype;	/* index identifying command */
81	char cmdname[];		/* name of command */
82};
83
84
85static struct tblentry *cmdtable[CMDTABLESIZE];
86static int cmdtable_cd = 0;	/* cmdtable contains cd-dependent entries */
87
88
89static void tryexec(char *, char **, char **);
90static void printentry(struct tblentry *, int);
91static struct tblentry *cmdlookup(const char *, int);
92static void delete_cmd_entry(void);
93static void addcmdentry(const char *, struct cmdentry *);
94
95
96
97/*
98 * Exec a program.  Never returns.  If you change this routine, you may
99 * have to change the find_command routine as well.
100 *
101 * The argv array may be changed and element argv[-1] should be writable.
102 */
103
104void
105shellexec(char **argv, char **envp, const char *path, int idx)
106{
107	char *cmdname;
108	const char *opt;
109	int e;
110
111	if (strchr(argv[0], '/') != NULL) {
112		tryexec(argv[0], argv, envp);
113		e = errno;
114	} else {
115		e = ENOENT;
116		while ((cmdname = padvance(&path, &opt, argv[0])) != NULL) {
117			if (--idx < 0 && opt == NULL) {
118				tryexec(cmdname, argv, envp);
119				if (errno != ENOENT && errno != ENOTDIR)
120					e = errno;
121				if (e == ENOEXEC)
122					break;
123			}
124			stunalloc(cmdname);
125		}
126	}
127
128	/* Map to POSIX errors */
129	if (e == ENOENT || e == ENOTDIR)
130		errorwithstatus(127, "%s: not found", argv[0]);
131	else
132		errorwithstatus(126, "%s: %s", argv[0], strerror(e));
133}
134
135
136static bool
137isbinary(const char *data, size_t len)
138{
139	const char *nul, *p;
140	bool hasletter;
141
142	nul = memchr(data, '\0', len);
143	if (nul == NULL)
144		return false;
145	/*
146	 * POSIX says we shall allow execution if the initial part intended
147	 * to be parsed by the shell consists of characters and does not
148	 * contain the NUL character. This allows concatenating a shell
149	 * script (ending with exec or exit) and a binary payload.
150	 *
151	 * In order to reject common binary files such as PNG images, check
152	 * that there is a lowercase letter or expansion before the last
153	 * newline before the NUL character, in addition to the check for
154	 * the newline character suggested by POSIX.
155	 */
156	hasletter = false;
157	for (p = data; *p != '\0'; p++) {
158		if ((*p >= 'a' && *p <= 'z') || *p == '$' || *p == '`')
159			hasletter = true;
160		if (hasletter && *p == '\n')
161			return false;
162	}
163	return true;
164}
165
166
167static void
168tryexec(char *cmd, char **argv, char **envp)
169{
170	int e, in;
171	ssize_t n;
172	char buf[256];
173
174	execve(cmd, argv, envp);
175	e = errno;
176	if (e == ENOEXEC) {
177		INTOFF;
178		in = open(cmd, O_RDONLY | O_NONBLOCK);
179		if (in != -1) {
180			n = pread(in, buf, sizeof buf, 0);
181			close(in);
182			if (n > 0 && isbinary(buf, n)) {
183				errno = ENOEXEC;
184				return;
185			}
186		}
187		*argv = cmd;
188		*--argv = __DECONST(char *, _PATH_BSHELL);
189		execve(_PATH_BSHELL, argv, envp);
190	}
191	errno = e;
192}
193
194/*
195 * Do a path search.  The variable path (passed by reference) should be
196 * set to the start of the path before the first call; padvance will update
197 * this value as it proceeds.  Successive calls to padvance will return
198 * the possible path expansions in sequence.  If popt is not NULL, options
199 * are processed: if an option (indicated by a percent sign) appears in
200 * the path entry then *popt will be set to point to it; else *popt will be
201 * set to NULL.  If popt is NULL, percent signs are not special.
202 */
203
204char *
205padvance(const char **path, const char **popt, const char *name)
206{
207	const char *p, *start;
208	char *q;
209	size_t len, namelen;
210
211	if (*path == NULL)
212		return NULL;
213	start = *path;
214	if (popt != NULL)
215		for (p = start; *p && *p != ':' && *p != '%'; p++)
216			; /* nothing */
217	else
218		for (p = start; *p && *p != ':'; p++)
219			; /* nothing */
220	namelen = strlen(name);
221	len = p - start + namelen + 2;	/* "2" is for '/' and '\0' */
222	STARTSTACKSTR(q);
223	CHECKSTRSPACE(len, q);
224	if (p != start) {
225		memcpy(q, start, p - start);
226		q += p - start;
227		*q++ = '/';
228	}
229	memcpy(q, name, namelen + 1);
230	if (popt != NULL) {
231		if (*p == '%') {
232			*popt = ++p;
233			while (*p && *p != ':')  p++;
234		} else
235			*popt = NULL;
236	}
237	if (*p == ':')
238		*path = p + 1;
239	else
240		*path = NULL;
241	return stalloc(len);
242}
243
244
245
246/*** Command hashing code ***/
247
248
249int
250hashcmd(int argc __unused, char **argv __unused)
251{
252	struct tblentry **pp;
253	struct tblentry *cmdp;
254	int c;
255	int verbose;
256	struct cmdentry entry;
257	char *name;
258	int errors;
259
260	errors = 0;
261	verbose = 0;
262	while ((c = nextopt("rv")) != '\0') {
263		if (c == 'r') {
264			clearcmdentry();
265		} else if (c == 'v') {
266			verbose++;
267		}
268	}
269	if (*argptr == NULL) {
270		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
271			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
272				if (cmdp->cmdtype == CMDNORMAL)
273					printentry(cmdp, verbose);
274			}
275		}
276		return 0;
277	}
278	while ((name = *argptr) != NULL) {
279		if ((cmdp = cmdlookup(name, 0)) != NULL
280		 && cmdp->cmdtype == CMDNORMAL)
281			delete_cmd_entry();
282		find_command(name, &entry, DO_ERR, pathval());
283		if (entry.cmdtype == CMDUNKNOWN)
284			errors = 1;
285		else if (verbose) {
286			cmdp = cmdlookup(name, 0);
287			if (cmdp != NULL)
288				printentry(cmdp, verbose);
289			else {
290				outfmt(out2, "%s: not found\n", name);
291				errors = 1;
292			}
293			flushall();
294		}
295		argptr++;
296	}
297	return errors;
298}
299
300
301static void
302printentry(struct tblentry *cmdp, int verbose)
303{
304	int idx;
305	const char *path, *opt;
306	char *name;
307
308	if (cmdp->cmdtype == CMDNORMAL) {
309		idx = cmdp->param.index;
310		path = pathval();
311		do {
312			name = padvance(&path, &opt, cmdp->cmdname);
313			stunalloc(name);
314		} while (--idx >= 0);
315		out1str(name);
316	} else if (cmdp->cmdtype == CMDBUILTIN) {
317		out1fmt("builtin %s", cmdp->cmdname);
318	} else if (cmdp->cmdtype == CMDFUNCTION) {
319		out1fmt("function %s", cmdp->cmdname);
320		if (verbose) {
321			INTOFF;
322			name = commandtext(getfuncnode(cmdp->param.func));
323			out1c(' ');
324			out1str(name);
325			ckfree(name);
326			INTON;
327		}
328#ifdef DEBUG
329	} else {
330		error("internal error: cmdtype %d", cmdp->cmdtype);
331#endif
332	}
333	out1c('\n');
334}
335
336
337
338/*
339 * Resolve a command name.  If you change this routine, you may have to
340 * change the shellexec routine as well.
341 */
342
343void
344find_command(const char *name, struct cmdentry *entry, int act,
345    const char *path)
346{
347	struct tblentry *cmdp, loc_cmd;
348	int idx;
349	const char *opt;
350	char *fullname;
351	struct stat statb;
352	int e;
353	int i;
354	int spec;
355	int cd;
356
357	/* If name contains a slash, don't use the hash table */
358	if (strchr(name, '/') != NULL) {
359		entry->cmdtype = CMDNORMAL;
360		entry->u.index = 0;
361		entry->special = 0;
362		return;
363	}
364
365	cd = 0;
366
367	/* If name is in the table, we're done */
368	if ((cmdp = cmdlookup(name, 0)) != NULL) {
369		if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
370			cmdp = NULL;
371		else
372			goto success;
373	}
374
375	/* Check for builtin next */
376	if ((i = find_builtin(name, &spec)) >= 0) {
377		INTOFF;
378		cmdp = cmdlookup(name, 1);
379		if (cmdp->cmdtype == CMDFUNCTION)
380			cmdp = &loc_cmd;
381		cmdp->cmdtype = CMDBUILTIN;
382		cmdp->param.index = i;
383		cmdp->special = spec;
384		INTON;
385		goto success;
386	}
387
388	/* We have to search path. */
389
390	e = ENOENT;
391	idx = -1;
392	for (;(fullname = padvance(&path, &opt, name)) != NULL;
393	    stunalloc(fullname)) {
394		idx++;
395		if (opt) {
396			if (strncmp(opt, "func", 4) == 0) {
397				/* handled below */
398			} else {
399				continue; /* ignore unimplemented options */
400			}
401		}
402		if (fullname[0] != '/')
403			cd = 1;
404		if (stat(fullname, &statb) < 0) {
405			if (errno != ENOENT && errno != ENOTDIR)
406				e = errno;
407			continue;
408		}
409		e = EACCES;	/* if we fail, this will be the error */
410		if (!S_ISREG(statb.st_mode))
411			continue;
412		if (opt) {		/* this is a %func directory */
413			readcmdfile(fullname, -1 /* verify */);
414			if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
415				error("%s not defined in %s", name, fullname);
416			stunalloc(fullname);
417			goto success;
418		}
419#ifdef notdef
420		if (statb.st_uid == geteuid()) {
421			if ((statb.st_mode & 0100) == 0)
422				goto loop;
423		} else if (statb.st_gid == getegid()) {
424			if ((statb.st_mode & 010) == 0)
425				goto loop;
426		} else {
427			if ((statb.st_mode & 01) == 0)
428				goto loop;
429		}
430#endif
431		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
432		INTOFF;
433		stunalloc(fullname);
434		cmdp = cmdlookup(name, 1);
435		if (cmdp->cmdtype == CMDFUNCTION)
436			cmdp = &loc_cmd;
437		cmdp->cmdtype = CMDNORMAL;
438		cmdp->param.index = idx;
439		cmdp->special = 0;
440		INTON;
441		goto success;
442	}
443
444	if (act & DO_ERR) {
445		if (e == ENOENT || e == ENOTDIR)
446			outfmt(out2, "%s: not found\n", name);
447		else
448			outfmt(out2, "%s: %s\n", name, strerror(e));
449	}
450	entry->cmdtype = CMDUNKNOWN;
451	entry->u.index = 0;
452	entry->special = 0;
453	return;
454
455success:
456	if (cd)
457		cmdtable_cd = 1;
458	entry->cmdtype = cmdp->cmdtype;
459	entry->u = cmdp->param;
460	entry->special = cmdp->special;
461}
462
463
464
465/*
466 * Search the table of builtin commands.
467 */
468
469int
470find_builtin(const char *name, int *special)
471{
472	const unsigned char *bp;
473	size_t len;
474
475	len = strlen(name);
476	for (bp = builtincmd ; *bp ; bp += 2 + bp[0]) {
477		if (bp[0] == len && memcmp(bp + 2, name, len) == 0) {
478			*special = (bp[1] & BUILTIN_SPECIAL) != 0;
479			return bp[1] & ~BUILTIN_SPECIAL;
480		}
481	}
482	return -1;
483}
484
485
486
487/*
488 * Called when a cd is done.  If any entry in cmdtable depends on the current
489 * directory, simply clear cmdtable completely.
490 */
491
492void
493hashcd(void)
494{
495	if (cmdtable_cd)
496		clearcmdentry();
497}
498
499
500
501/*
502 * Called before PATH is changed.  The argument is the new value of PATH;
503 * pathval() still returns the old value at this point.  Called with
504 * interrupts off.
505 */
506
507void
508changepath(const char *newval __unused)
509{
510	clearcmdentry();
511}
512
513
514/*
515 * Clear out cached utility locations.
516 */
517
518void
519clearcmdentry(void)
520{
521	struct tblentry **tblp;
522	struct tblentry **pp;
523	struct tblentry *cmdp;
524
525	INTOFF;
526	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
527		pp = tblp;
528		while ((cmdp = *pp) != NULL) {
529			if (cmdp->cmdtype == CMDNORMAL) {
530				*pp = cmdp->next;
531				ckfree(cmdp);
532			} else {
533				pp = &cmdp->next;
534			}
535		}
536	}
537	cmdtable_cd = 0;
538	INTON;
539}
540
541
542static unsigned int
543hashname(const char *p)
544{
545	unsigned int hashval;
546
547	hashval = (unsigned char)*p << 4;
548	while (*p)
549		hashval += *p++;
550
551	return (hashval % CMDTABLESIZE);
552}
553
554
555/*
556 * Locate a command in the command hash table.  If "add" is nonzero,
557 * add the command to the table if it is not already present.  The
558 * variable "lastcmdentry" is set to point to the address of the link
559 * pointing to the entry, so that delete_cmd_entry can delete the
560 * entry.
561 */
562
563static struct tblentry **lastcmdentry;
564
565
566static struct tblentry *
567cmdlookup(const char *name, int add)
568{
569	struct tblentry *cmdp;
570	struct tblentry **pp;
571	size_t len;
572
573	pp = &cmdtable[hashname(name)];
574	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
575		if (equal(cmdp->cmdname, name))
576			break;
577		pp = &cmdp->next;
578	}
579	if (add && cmdp == NULL) {
580		INTOFF;
581		len = strlen(name);
582		cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
583		cmdp->next = NULL;
584		cmdp->cmdtype = CMDUNKNOWN;
585		memcpy(cmdp->cmdname, name, len + 1);
586		INTON;
587	}
588	lastcmdentry = pp;
589	return cmdp;
590}
591
592const void *
593itercmd(const void *entry, struct cmdentry *result)
594{
595	const struct tblentry *e = entry;
596	size_t i = 0;
597
598	if (e != NULL) {
599		if (e->next != NULL) {
600			e = e->next;
601			goto success;
602		}
603		i = hashname(e->cmdname) + 1;
604	}
605	for (; i < CMDTABLESIZE; i++)
606		if ((e = cmdtable[i]) != NULL)
607			goto success;
608
609	return (NULL);
610success:
611	result->cmdtype = e->cmdtype;
612	result->cmdname = e->cmdname;
613
614	return (e);
615}
616
617/*
618 * Delete the command entry returned on the last lookup.
619 */
620
621static void
622delete_cmd_entry(void)
623{
624	struct tblentry *cmdp;
625
626	INTOFF;
627	cmdp = *lastcmdentry;
628	*lastcmdentry = cmdp->next;
629	ckfree(cmdp);
630	INTON;
631}
632
633
634
635/*
636 * Add a new command entry, replacing any existing command entry for
637 * the same name.
638 */
639
640static void
641addcmdentry(const char *name, struct cmdentry *entry)
642{
643	struct tblentry *cmdp;
644
645	INTOFF;
646	cmdp = cmdlookup(name, 1);
647	if (cmdp->cmdtype == CMDFUNCTION) {
648		unreffunc(cmdp->param.func);
649	}
650	cmdp->cmdtype = entry->cmdtype;
651	cmdp->param = entry->u;
652	cmdp->special = entry->special;
653	INTON;
654}
655
656
657/*
658 * Define a shell function.
659 */
660
661void
662defun(const char *name, union node *func)
663{
664	struct cmdentry entry;
665
666	INTOFF;
667	entry.cmdtype = CMDFUNCTION;
668	entry.u.func = copyfunc(func);
669	entry.special = 0;
670	addcmdentry(name, &entry);
671	INTON;
672}
673
674
675/*
676 * Delete a function if it exists.
677 * Called with interrupts off.
678 */
679
680int
681unsetfunc(const char *name)
682{
683	struct tblentry *cmdp;
684
685	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
686		unreffunc(cmdp->param.func);
687		delete_cmd_entry();
688		return (0);
689	}
690	return (0);
691}
692
693
694/*
695 * Check if a function by a certain name exists.
696 */
697int
698isfunc(const char *name)
699{
700	struct tblentry *cmdp;
701	cmdp = cmdlookup(name, 0);
702	return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
703}
704
705
706static void
707print_absolute_path(const char *name)
708{
709	const char *pwd;
710
711	if (*name != '/' && (pwd = lookupvar("PWD")) != NULL && *pwd != '\0') {
712		out1str(pwd);
713		if (strcmp(pwd, "/") != 0)
714			outcslow('/', out1);
715	}
716	out1str(name);
717	outcslow('\n', out1);
718}
719
720
721/*
722 * Shared code for the following builtin commands:
723 *    type, command -v, command -V
724 */
725
726int
727typecmd_impl(int argc, char **argv, int cmd, const char *path)
728{
729	struct cmdentry entry;
730	struct tblentry *cmdp;
731	const char *const *pp;
732	struct alias *ap;
733	int i;
734	int error1 = 0;
735
736	if (path != pathval())
737		clearcmdentry();
738
739	for (i = 1; i < argc; i++) {
740		/* First look at the keywords */
741		for (pp = parsekwd; *pp; pp++)
742			if (**pp == *argv[i] && equal(*pp, argv[i]))
743				break;
744
745		if (*pp) {
746			if (cmd == TYPECMD_SMALLV)
747				out1fmt("%s\n", argv[i]);
748			else
749				out1fmt("%s is a shell keyword\n", argv[i]);
750			continue;
751		}
752
753		/* Then look at the aliases */
754		if ((ap = lookupalias(argv[i], 1)) != NULL) {
755			if (cmd == TYPECMD_SMALLV) {
756				out1fmt("alias %s=", argv[i]);
757				out1qstr(ap->val);
758				outcslow('\n', out1);
759			} else
760				out1fmt("%s is an alias for %s\n", argv[i],
761				    ap->val);
762			continue;
763		}
764
765		/* Then check if it is a tracked alias */
766		if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
767			entry.cmdtype = cmdp->cmdtype;
768			entry.u = cmdp->param;
769			entry.special = cmdp->special;
770		}
771		else {
772			/* Finally use brute force */
773			find_command(argv[i], &entry, 0, path);
774		}
775
776		switch (entry.cmdtype) {
777		case CMDNORMAL: {
778			if (strchr(argv[i], '/') == NULL) {
779				const char *path2 = path;
780				const char *opt2;
781				char *name;
782				int j = entry.u.index;
783				do {
784					name = padvance(&path2, &opt2, argv[i]);
785					stunalloc(name);
786				} while (--j >= 0);
787				if (cmd != TYPECMD_SMALLV)
788					out1fmt("%s is%s ", argv[i],
789					    (cmdp && cmd == TYPECMD_TYPE) ?
790						" a tracked alias for" : "");
791				print_absolute_path(name);
792			} else {
793				if (eaccess(argv[i], X_OK) == 0) {
794					if (cmd != TYPECMD_SMALLV)
795						out1fmt("%s is ", argv[i]);
796					print_absolute_path(argv[i]);
797				} else {
798					if (cmd != TYPECMD_SMALLV)
799						outfmt(out2, "%s: %s\n",
800						    argv[i], strerror(errno));
801					error1 |= 127;
802				}
803			}
804			break;
805		}
806		case CMDFUNCTION:
807			if (cmd == TYPECMD_SMALLV)
808				out1fmt("%s\n", argv[i]);
809			else
810				out1fmt("%s is a shell function\n", argv[i]);
811			break;
812
813		case CMDBUILTIN:
814			if (cmd == TYPECMD_SMALLV)
815				out1fmt("%s\n", argv[i]);
816			else if (entry.special)
817				out1fmt("%s is a special shell builtin\n",
818				    argv[i]);
819			else
820				out1fmt("%s is a shell builtin\n", argv[i]);
821			break;
822
823		default:
824			if (cmd != TYPECMD_SMALLV)
825				outfmt(out2, "%s: not found\n", argv[i]);
826			error1 |= 127;
827			break;
828		}
829	}
830
831	if (path != pathval())
832		clearcmdentry();
833
834	return error1;
835}
836
837/*
838 * Locate and print what a word is...
839 */
840
841int
842typecmd(int argc, char **argv)
843{
844	if (argc > 2 && strcmp(argv[1], "--") == 0)
845		argc--, argv++;
846	return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
847}
848