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