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