source: trunk/minix/commands/ash/exec.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 17.2 KB
Line 
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * 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
37#ifndef lint
38static char sccsid[] = "@(#)exec.c 5.2 (Berkeley) 3/13/91";
39#endif /* not lint */
40
41/*
42 * When commands are first encountered, they are entered in a hash table.
43 * This ensures that a full path search will not have to be done for them
44 * on each invocation.
45 *
46 * We should investigate converting to a linear search, even though that
47 * would make the command name "hash" a misnomer.
48 */
49
50#include "shell.h"
51#include "main.h"
52#include "nodes.h"
53#include "parser.h"
54#include "redir.h"
55#include "eval.h"
56#include "exec.h"
57#include "builtins.h"
58#include "var.h"
59#include "options.h"
60#include "input.h"
61#include "output.h"
62#include "syntax.h"
63#include "memalloc.h"
64#include "error.h"
65#include "init.h"
66#include "mystring.h"
67#include <sys/types.h>
68#include <sys/stat.h>
69#include <fcntl.h>
70#include <errno.h>
71#include <limits.h>
72
73
74#define CMDTABLESIZE 31 /* should be prime */
75#define ARB 1 /* actual size determined at run time */
76
77
78
79struct tblentry {
80 struct tblentry *next; /* next entry in hash chain */
81 union param param; /* definition of builtin function */
82 short cmdtype; /* index identifying command */
83 char rehash; /* if set, cd done since entry created */
84 char cmdname[ARB]; /* name of command */
85};
86
87
88STATIC struct tblentry *cmdtable[CMDTABLESIZE];
89STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
90
91
92#ifdef __STDC__
93STATIC void tryexec(char *, char **, char **);
94STATIC void execinterp(char **, char **);
95STATIC void printentry(struct tblentry *);
96STATIC void clearcmdentry(int);
97STATIC struct tblentry *cmdlookup(char *, int);
98STATIC void delete_cmd_entry(void);
99#else
100STATIC void tryexec();
101STATIC void execinterp();
102STATIC void printentry();
103STATIC void clearcmdentry();
104STATIC struct tblentry *cmdlookup();
105STATIC void delete_cmd_entry();
106#endif
107
108
109
110/*
111 * Exec a program. Never returns. If you change this routine, you may
112 * have to change the find_command routine as well.
113 */
114
115void
116shellexec(argv, envp, path, index)
117 char **argv, **envp;
118 char *path;
119 {
120 char *cmdname;
121 int e;
122
123 if (strchr(argv[0], '/') != NULL) {
124 tryexec(argv[0], argv, envp);
125 e = errno;
126 } else {
127 e = ENOENT;
128 while ((cmdname = padvance(&path, argv[0])) != NULL) {
129 if (--index < 0 && pathopt == NULL) {
130 tryexec(cmdname, argv, envp);
131 if (errno != ENOENT && errno != ENOTDIR)
132 e = errno;
133 }
134 stunalloc(cmdname);
135 }
136 }
137 error2(argv[0], errmsg(e, E_EXEC));
138}
139
140
141STATIC void
142tryexec(cmd, argv, envp)
143 char *cmd;
144 char **argv;
145 char **envp;
146 {
147 int e;
148 char *p;
149
150#ifdef SYSV
151 do {
152 execve(cmd, argv, envp);
153 } while (errno == EINTR);
154#else
155 execve(cmd, argv, envp);
156#endif
157#if HASHBANG
158 e = errno;
159 if (e == ENOEXEC) {
160 initshellproc();
161 setinputfile(cmd, 0);
162 commandname = arg0 = savestr(argv[0]);
163#ifndef BSD
164 pgetc(); pungetc(); /* fill up input buffer */
165 p = parsenextc;
166 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
167 argv[0] = cmd;
168 execinterp(argv, envp);
169 }
170#endif
171 setparam(argv + 1);
172 exraise(EXSHELLPROC);
173 /*NOTREACHED*/
174 }
175 errno = e;
176#endif
177}
178
179
180#if !defined(BSD) && HASHBANG
181/*
182 * Execute an interpreter introduced by "#!", for systems where this
183 * feature has not been built into the kernel. If the interpreter is
184 * the shell, return (effectively ignoring the "#!"). If the execution
185 * of the interpreter fails, exit.
186 *
187 * This code peeks inside the input buffer in order to avoid actually
188 * reading any input. It would benefit from a rewrite.
189 */
190
191#define NEWARGS 5
192
193STATIC void
194execinterp(argv, envp)
195 char **argv, **envp;
196 {
197 int n;
198 char *inp;
199 char *outp;
200 char c;
201 char *p;
202 char **ap;
203 char *newargs[NEWARGS];
204 int i;
205 char **ap2;
206 char **new;
207
208 n = parsenleft - 2;
209 inp = parsenextc + 2;
210 ap = newargs;
211 for (;;) {
212 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
213 inp++;
214 if (n < 0)
215 goto bad;
216 if ((c = *inp++) == '\n')
217 break;
218 if (ap == &newargs[NEWARGS])
219bad: error("Bad #! line");
220 STARTSTACKSTR(outp);
221 do {
222 STPUTC(c, outp);
223 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
224 STPUTC('\0', outp);
225 n++, inp--;
226 *ap++ = grabstackstr(outp);
227 }
228#if !__minix
229 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
230 p = newargs[0];
231 for (;;) {
232 if (equal(p, "sh") || equal(p, "ash")) {
233 return;
234 }
235 while (*p != '/') {
236 if (*p == '\0')
237 goto break2;
238 p++;
239 }
240 p++;
241 }
242break2:;
243 }
244#endif
245 i = (char *)ap - (char *)newargs; /* size in bytes */
246 if (i == 0)
247 error("Bad #! line");
248 for (ap2 = argv ; *ap2++ != NULL ; );
249 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
250 ap = newargs, ap2 = new;
251 while ((i -= sizeof (char **)) >= 0)
252 *ap2++ = *ap++;
253 ap = argv;
254 while (*ap2++ = *ap++);
255 shellexec(new, envp, pathval(), 0);
256}
257#endif
258
259
260
261/*
262 * Do a path search. The variable path (passed by reference) should be
263 * set to the start of the path before the first call; padvance will update
264 * this value as it proceeds. Successive calls to padvance will return
265 * the possible path expansions in sequence. If an option (indicated by
266 * a percent sign) appears in the path entry then the global variable
267 * pathopt will be set to point to it; otherwise pathopt will be set to
268 * NULL.
269 */
270
271char *pathopt;
272
273char *
274padvance(path, name)
275 char **path;
276 char *name;
277 {
278 register char *p, *q;
279 char *start;
280 int len;
281
282 if (*path == NULL)
283 return NULL;
284 start = *path;
285 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
286 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
287 while (stackblocksize() < len)
288 growstackblock();
289 q = stackblock();
290 if (p != start) {
291 bcopy(start, q, p - start);
292 q += p - start;
293 *q++ = '/';
294 }
295 strcpy(q, name);
296 pathopt = NULL;
297 if (*p == '%') {
298 pathopt = ++p;
299 while (*p && *p != ':') p++;
300 }
301 if (*p == ':')
302 *path = p + 1;
303 else
304 *path = NULL;
305 return stalloc(len);
306}
307
308
309
310/*** Command hashing code ***/
311
312
313hashcmd(argc, argv) char **argv; {
314 struct tblentry **pp;
315 struct tblentry *cmdp;
316 int c;
317 int verbose;
318 struct cmdentry entry;
319 char *name;
320
321 if (argc <= 1) {
322 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
323 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
324 printentry(cmdp);
325 }
326 }
327 return 0;
328 }
329 verbose = 0;
330 while ((c = nextopt("rv")) != '\0') {
331 if (c == 'r') {
332 clearcmdentry(0);
333 } else if (c == 'v') {
334 verbose++;
335 }
336 }
337 while ((name = *argptr) != NULL) {
338 if ((cmdp = cmdlookup(name, 0)) != NULL
339 && (cmdp->cmdtype == CMDNORMAL
340 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
341 delete_cmd_entry();
342 find_command(name, &entry, 1);
343 if (verbose) {
344 if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
345 cmdp = cmdlookup(name, 0);
346 printentry(cmdp);
347 }
348 flushall();
349 }
350 argptr++;
351 }
352 return 0;
353}
354
355
356STATIC void
357printentry(cmdp)
358 struct tblentry *cmdp;
359 {
360 int index;
361 char *path;
362 char *name;
363
364 if (cmdp->cmdtype == CMDNORMAL) {
365 index = cmdp->param.index;
366 path = pathval();
367 do {
368 name = padvance(&path, cmdp->cmdname);
369 stunalloc(name);
370 } while (--index >= 0);
371 out1str(name);
372 } else if (cmdp->cmdtype == CMDBUILTIN) {
373 out1fmt("builtin %s", cmdp->cmdname);
374 } else if (cmdp->cmdtype == CMDFUNCTION) {
375 out1fmt("function %s", cmdp->cmdname);
376#if DEBUG
377 } else {
378 error("internal error: cmdtype %d", cmdp->cmdtype);
379#endif
380 }
381 if (cmdp->rehash)
382 out1c('*');
383 out1c('\n');
384}
385
386
387
388/*
389 * Resolve a command name. If you change this routine, you may have to
390 * change the shellexec routine as well.
391 */
392
393void
394find_command(name, entry, printerr)
395 char *name;
396 struct cmdentry *entry;
397 {
398 struct tblentry *cmdp;
399 int index;
400 int prev;
401 char *path;
402 char *fullname;
403 struct stat statb;
404 int e;
405 int i;
406
407 /* If name contains a slash, don't use the hash table */
408 if (strchr(name, '/') != NULL) {
409 entry->cmdtype = CMDNORMAL;
410 entry->u.index = 0;
411 return;
412 }
413
414 /* If name is in the table, and not invalidated by cd, we're done */
415 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
416 goto success;
417
418 /* If %builtin not in path, check for builtin next */
419 if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
420 INTOFF;
421 cmdp = cmdlookup(name, 1);
422 cmdp->cmdtype = CMDBUILTIN;
423 cmdp->param.index = i;
424 INTON;
425 goto success;
426 }
427
428 /* We have to search path. */
429 prev = -1; /* where to start */
430 if (cmdp) { /* doing a rehash */
431 if (cmdp->cmdtype == CMDBUILTIN)
432 prev = builtinloc;
433 else
434 prev = cmdp->param.index;
435 }
436
437 path = pathval();
438 e = ENOENT;
439 index = -1;
440loop:
441 while ((fullname = padvance(&path, name)) != NULL) {
442 stunalloc(fullname);
443 index++;
444 if (pathopt) {
445 if (prefix("builtin", pathopt)) {
446 if ((i = find_builtin(name)) < 0)
447 goto loop;
448 INTOFF;
449 cmdp = cmdlookup(name, 1);
450 cmdp->cmdtype = CMDBUILTIN;
451 cmdp->param.index = i;
452 INTON;
453 goto success;
454 } else if (prefix("func", pathopt)) {
455 /* handled below */
456 } else {
457 goto loop; /* ignore unimplemented options */
458 }
459 }
460 /* if rehash, don't redo absolute path names */
461 if (fullname[0] == '/' && index <= prev) {
462 if (index < prev)
463 goto loop;
464 TRACE(("searchexec \"%s\": no change\n", name));
465 goto success;
466 }
467 while (stat(fullname, &statb) < 0) {
468#ifdef SYSV
469 if (errno == EINTR)
470 continue;
471#endif
472 if (errno != ENOENT && errno != ENOTDIR)
473 e = errno;
474 goto loop;
475 }
476 e = EACCES; /* if we fail, this will be the error */
477 if ((statb.st_mode & S_IFMT) != S_IFREG)
478 goto loop;
479 if (pathopt) { /* this is a %func directory */
480 stalloc(strlen(fullname) + 1);
481 readcmdfile(fullname);
482 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
483 error("%s not defined in %s", name, fullname);
484 stunalloc(fullname);
485 goto success;
486 }
487 if (statb.st_uid == geteuid()) {
488 if ((statb.st_mode & 0100) == 0)
489 goto loop;
490 } else if (statb.st_gid == getegid()) {
491 if ((statb.st_mode & 010) == 0)
492 goto loop;
493 } else {
494#if __minix_vmd || defined(BSD)
495 gid_t group_list[NGROUPS_MAX];
496 int ngroups, i;
497
498 ngroups = getgroups(NGROUPS_MAX, group_list);
499
500 for (i = 0; i < ngroups; i++) {
501 if (statb.st_gid == group_list[i]) break;
502 }
503 if (i < ngroups) {
504 if ((statb.st_mode & 010) == 0)
505 goto loop;
506 } else
507#endif
508 if ((statb.st_mode & 01) == 0)
509 goto loop;
510 }
511 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
512 INTOFF;
513 cmdp = cmdlookup(name, 1);
514 cmdp->cmdtype = CMDNORMAL;
515 cmdp->param.index = index;
516 INTON;
517 goto success;
518 }
519
520 /* We failed. If there was an entry for this command, delete it */
521 if (cmdp)
522 delete_cmd_entry();
523 if (printerr)
524 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
525 entry->cmdtype = CMDUNKNOWN;
526 return;
527
528success:
529 cmdp->rehash = 0;
530 entry->cmdtype = cmdp->cmdtype;
531 entry->u = cmdp->param;
532}
533
534
535
536/*
537 * Search the table of builtin commands.
538 */
539
540int
541find_builtin(name)
542 char *name;
543 {
544 const register struct builtincmd *bp;
545
546 for (bp = builtincmd ; bp->name ; bp++) {
547 if (*bp->name == *name && equal(bp->name, name))
548 return bp->code;
549 }
550 return -1;
551}
552
553
554
555/*
556 * Called when a cd is done. Marks all commands so the next time they
557 * are executed they will be rehashed.
558 */
559
560void
561hashcd() {
562 struct tblentry **pp;
563 struct tblentry *cmdp;
564
565 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
566 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
567 if (cmdp->cmdtype == CMDNORMAL
568 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)
569 cmdp->rehash = 1;
570 }
571 }
572}
573
574
575
576/*
577 * Called before PATH is changed. The argument is the new value of PATH;
578 * pathval() still returns the old value at this point. Called with
579 * interrupts off.
580 */
581
582void
583changepath(newval)
584 char *newval;
585 {
586 char *old, *new;
587 int index;
588 int firstchange;
589 int bltin;
590
591 old = pathval();
592 new = newval;
593 firstchange = 9999; /* assume no change */
594 index = 0;
595 bltin = -1;
596 for (;;) {
597 if (*old != *new) {
598 firstchange = index;
599 if (*old == '\0' && *new == ':'
600 || *old == ':' && *new == '\0')
601 firstchange++;
602 old = new; /* ignore subsequent differences */
603 }
604 if (*new == '\0')
605 break;
606 if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
607 bltin = index;
608 if (*new == ':') {
609 index++;
610 }
611 new++, old++;
612 }
613 if (builtinloc < 0 && bltin >= 0)
614 builtinloc = bltin; /* zap builtins */
615 if (builtinloc >= 0 && bltin < 0)
616 firstchange = 0;
617 clearcmdentry(firstchange);
618 builtinloc = bltin;
619}
620
621
622/*
623 * Clear out command entries. The argument specifies the first entry in
624 * PATH which has changed.
625 */
626
627STATIC void
628clearcmdentry(firstchange) {
629 struct tblentry **tblp;
630 struct tblentry **pp;
631 struct tblentry *cmdp;
632
633 INTOFF;
634 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
635 pp = tblp;
636 while ((cmdp = *pp) != NULL) {
637 if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange
638 || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) {
639 *pp = cmdp->next;
640 ckfree(cmdp);
641 } else {
642 pp = &cmdp->next;
643 }
644 }
645 }
646 INTON;
647}
648
649
650/*
651 * Delete all functions.
652 */
653
654#ifdef mkinit
655MKINIT void deletefuncs();
656
657SHELLPROC {
658 deletefuncs();
659}
660#endif
661
662void
663deletefuncs() {
664 struct tblentry **tblp;
665 struct tblentry **pp;
666 struct tblentry *cmdp;
667
668 INTOFF;
669 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
670 pp = tblp;
671 while ((cmdp = *pp) != NULL) {
672 if (cmdp->cmdtype == CMDFUNCTION) {
673 *pp = cmdp->next;
674 freefunc(cmdp->param.func);
675 ckfree(cmdp);
676 } else {
677 pp = &cmdp->next;
678 }
679 }
680 }
681 INTON;
682}
683
684
685
686/*
687 * Locate a command in the command hash table. If "add" is nonzero,
688 * add the command to the table if it is not already present. The
689 * variable "lastcmdentry" is set to point to the address of the link
690 * pointing to the entry, so that delete_cmd_entry can delete the
691 * entry.
692 */
693
694struct tblentry **lastcmdentry;
695
696
697STATIC struct tblentry *
698cmdlookup(name, add)
699 char *name;
700 {
701 int hashval;
702 register char *p;
703 struct tblentry *cmdp;
704 struct tblentry **pp;
705
706 p = name;
707 hashval = *p << 4;
708 while (*p)
709 hashval += *p++;
710 hashval &= 0x7FFF;
711 pp = &cmdtable[hashval % CMDTABLESIZE];
712 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
713 if (equal(cmdp->cmdname, name))
714 break;
715 pp = &cmdp->next;
716 }
717 if (add && cmdp == NULL) {
718 INTOFF;
719 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
720 + strlen(name) + 1);
721 cmdp->next = NULL;
722 cmdp->cmdtype = CMDUNKNOWN;
723 cmdp->rehash = 0;
724 strcpy(cmdp->cmdname, name);
725 INTON;
726 }
727 lastcmdentry = pp;
728 return cmdp;
729}
730
731
732/*
733 * Delete the command entry returned on the last lookup.
734 */
735
736STATIC void
737delete_cmd_entry() {
738 struct tblentry *cmdp;
739
740 INTOFF;
741 cmdp = *lastcmdentry;
742 *lastcmdentry = cmdp->next;
743 ckfree(cmdp);
744 INTON;
745}
746
747
748
749#ifdef notdef
750void
751getcmdentry(name, entry)
752 char *name;
753 struct cmdentry *entry;
754 {
755 struct tblentry *cmdp = cmdlookup(name, 0);
756
757 if (cmdp) {
758 entry->u = cmdp->param;
759 entry->cmdtype = cmdp->cmdtype;
760 } else {
761 entry->cmdtype = CMDUNKNOWN;
762 entry->u.index = 0;
763 }
764}
765#endif
766
767
768/*
769 * Add a new command entry, replacing any existing command entry for
770 * the same name.
771 */
772
773void
774addcmdentry(name, entry)
775 char *name;
776 struct cmdentry *entry;
777 {
778 struct tblentry *cmdp;
779
780 INTOFF;
781 cmdp = cmdlookup(name, 1);
782 if (cmdp->cmdtype == CMDFUNCTION) {
783 freefunc(cmdp->param.func);
784 }
785 cmdp->cmdtype = entry->cmdtype;
786 cmdp->param = entry->u;
787 INTON;
788}
789
790
791/*
792 * Define a shell function.
793 */
794
795void
796defun(name, func)
797 char *name;
798 union node *func;
799 {
800 struct cmdentry entry;
801
802 INTOFF;
803 entry.cmdtype = CMDFUNCTION;
804 entry.u.func = copyfunc(func);
805 addcmdentry(name, &entry);
806 INTON;
807}
808
809
810/*
811 * Delete a function if it exists.
812 */
813
814void
815unsetfunc(name)
816 char *name;
817 {
818 struct tblentry *cmdp;
819
820 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
821 freefunc(cmdp->param.func);
822 delete_cmd_entry();
823 }
824}
Note: See TracBrowser for help on using the repository browser.