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