#include "signal.h" #include "errno.h" #include "setjmp.h" #include "sh.h" /* -------- exec.c -------- */ /* #include "sh.h" */ /* * execute tree */ static char *signame[] = { "Signal 0", "Hangup", NULL, /* interrupt */ "Quit", "Illegal instruction", "Trace/BPT trap", "abort", "EMT trap", "Floating exception", "Killed", "Bus error", "Memory fault", "Bad system call", NULL, /* broken pipe */ "Alarm clock", "Terminated", }; #define NSIGNAL (sizeof(signame)/sizeof(signame[0])) static struct op *findcase(); static void brkset(); static void echo(); static int forkexec(); static int parent(); int execute(t, pin, pout, act) register struct op *t; int *pin, *pout; int act; { register struct op *t1; int i, pv[2], rv, child, a; char *cp, **wp, **wp2; struct var *vp; struct brkcon bc; if (t == NULL) return(0); rv = 0; a = areanum++; wp = (wp2 = t->words) != NULL? eval(wp2, DOALL): NULL; switch(t->type) { case TPAREN: case TCOM: rv = forkexec(t, pin, pout, act, wp, &child); if (child) { exstat = rv; leave(); } break; case TPIPE: if ((rv = openpipe(pv)) < 0) break; pv[0] = remap(pv[0]); pv[1] = remap(pv[1]); (void) execute(t->left, pin, pv, 0); rv = execute(t->right, pv, pout, 0); break; case TLIST: (void) execute(t->left, pin, pout, 0); rv = execute(t->right, pin, pout, 0); break; case TASYNC: i = parent(); if (i != 0) { if (i != -1) { if (pin != NULL) closepipe(pin); if (talking) { prs(putn(i)); prs("\n"); } } else rv = -1; setstatus(rv); } else { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); if (talking) signal(SIGTERM, SIG_DFL); talking = 0; if (pin == NULL) { close(0); open("/dev/null", 0); } exit(execute(t->left, pin, pout, FEXEC)); } break; case TOR: case TAND: rv = execute(t->left, pin, pout, 0); if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND)) rv = execute(t1, pin, pout, 0); break; case TFOR: if (wp == NULL) { wp = dolv+1; if ((i = dolc-1) < 0) i = 0; } else i = -1; vp = lookup(t->str); while (setjmp(bc.brkpt)) if (isbreak) goto broken; brkset(&bc); for (t1 = t->left; i-- && *wp != NULL;) { setval(vp, *wp++); rv = execute(t1, pin, pout, 0); } brklist = brklist->nextlev; break; case TWHILE: case TUNTIL: while (setjmp(bc.brkpt)) if (isbreak) goto broken; brkset(&bc); t1 = t->left; while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE)) rv = execute(t->right, pin, pout, 0); brklist = brklist->nextlev; break; case TIF: case TELIF: rv = !execute(t->left, pin, pout, 0)? execute(t->right->left, pin, pout, 0): execute(t->right->right, pin, pout, 0); break; case TCASE: if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0) cp = ""; if ((t1 = findcase(t->left, cp)) != NULL) rv = execute(t1, pin, pout, 0); break; case TBRACE: /* if (iopp = t->ioact) while (*iopp) if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) { rv = -1; break; } */ if (rv >= 0 && (t1 = t->left)) rv = execute(t1, pin, pout, 0); break; } broken: t->words = wp2; isbreak = 0; freearea(areanum); areanum = a; if (intr) { closeall(); fail(); } return(rv); } static int forkexec(t, pin, pout, act, wp, pforked) register struct op *t; int *pin, *pout; int act; char **wp; int *pforked; { int i, rv, (*shcom)(); int doexec(); register int f; char *cp; struct ioword **iopp; int resetsig; resetsig = 0; *pforked = 0; shcom = NULL; rv = -1; /* system-detected error */ if (t->type == TCOM) { /* strip all initial assignments */ /* not correct wrt PATH=yyy command etc */ if (flag['x']) echo(wp); while ((cp = *wp++) != NULL && assign(cp, COPYV)) ; wp--; if (cp == NULL && t->ioact == NULL) return(setstatus(0)); else shcom = inbuilt(cp); } t->words = wp; f = act; if (shcom == NULL && (f & FEXEC) == 0) { i = parent(); if (i != 0) { if (i == -1) return(rv); if (pin != NULL) closepipe(pin); return(pout==NULL? setstatus(waitfor(i,0)): 0); } if (talking) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); resetsig = 1; } talking = 0; intr = 0; (*pforked)++; brklist = 0; execflg = 0; } #ifdef COMPIPE if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) { err("piping to/from shell builtins not yet done"); return(-1); } #endif if (pin != NULL) { dup2(pin[0], 0); closepipe(pin); } if (pout != NULL) { dup2(pout[1], 1); closepipe(pout); } if ((iopp = t->ioact) != NULL) { if (shcom != NULL && shcom != doexec) { prs(cp); err(": cannot redirect shell command"); return(-1); } while (*iopp) if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) return(rv); } if (shcom) return(setstatus((*shcom)(t))); /* should use FIOCEXCL */ for (i=FDBASE; i<NOFILE; i++) close(i); if (t->type == TPAREN) exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); if (resetsig) { signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); } if (wp[0] == NULL) exit(0); cp = rexecve(wp[0], wp, makenv(wp)); prs(wp[0]); prs(": "); warn(cp); if (!execflg) trap[0] = NULL; leave(); /* NOTREACHED */ } /* * common actions when creating a new child */ static int parent() { register int i; i = fork(); if (i != 0) { if (i == -1) warn("try again"); setval(lookup("!"), putn(i)); } return(i); } /* * 0< 1> are ignored as required * within pipelines. */ iosetup(iop, pipein, pipeout) register struct ioword *iop; int pipein, pipeout; { register u; char *cp, *msg; if (iop->io_unit == IODEFAULT) /* take default */ iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1; if (pipein && iop->io_unit == 0) return(0); if (pipeout && iop->io_unit == 1) return(0); msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create"; if ((iop->io_flag & IOHERE) == 0) { cp = iop->io_un.io_name; if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL) return(1); } if (iop->io_flag & IODUP) { if (cp[1] || !digit(*cp) && *cp != '-') { prs(cp); err(": illegal >& argument"); return(1); } if (*cp == '-') iop->io_flag = IOCLOSE; iop->io_flag &= ~(IOREAD|IOWRITE); } switch (iop->io_flag) { case IOREAD: u = open(cp, 0); break; case IOHERE: case IOHERE|IOXHERE: u = herein(iop->io_un.io_here, iop->io_flag&IOXHERE); cp = "here file"; break; case IOWRITE|IOCAT: if ((u = open(cp, 1)) >= 0) { lseek(u, (long)0, 2); break; } case IOWRITE: u = creat(cp, 0666); break; case IODUP: u = dup2(*cp-'0', iop->io_unit); break; case IOCLOSE: close(iop->io_unit); return(0); } if (u < 0) { prs(cp); prs(": cannot "); warn(msg); return(1); } else { if (u != iop->io_unit) { dup2(u, iop->io_unit); close(u); } } return(0); } static void echo(wp) register char **wp; { register i; prs("+"); for (i=0; wp[i]; i++) { if (i) prs(" "); prs(wp[i]); } prs("\n"); } static struct op ** find1case(t, w) struct op *t; char *w; { register struct op *t1; struct op **tp; register char **wp, *cp; if (t == NULL) return(NULL); if (t->type == TLIST) { if ((tp = find1case(t->left, w)) != NULL) return(tp); t1 = t->right; /* TPAT */ } else t1 = t; for (wp = t1->words; *wp;) if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) return(&t1->left); return(NULL); } static struct op * findcase(t, w) struct op *t; char *w; { register struct op **tp; return((tp = find1case(t, w)) != NULL? *tp: NULL); } /* * Enter a new loop level (marked for break/continue). */ static void brkset(bc) struct brkcon *bc; { bc->nextlev = brklist; brklist = bc; } /* * Wait for the last process created. * Print a message for each process found * that was killed by a signal. * Ignore interrupt signals while waiting * unless `canintr' is true. */ int waitfor(lastpid, canintr) register int lastpid; int canintr; { register int pid, rv; int s; rv = 0; do { pid = wait(&s); if (pid == -1) { if (errno != EINTR || canintr) break; } else { if ((rv = WAITSIG(s)) != 0) { if (rv < NSIGNAL) { if (signame[rv] != NULL) { if (pid != lastpid) { prn(pid); prs(": "); } prs(signame[rv]); } } else { if (pid != lastpid) { prn(pid); prs(": "); } prs("Signal "); prn(rv); prs(" "); } if (WAITCORE(s)) prs(" - core dumped"); prs("\n"); rv = -1; } else rv = WAITVAL(s); } /* Special patch for MINIX: sync before each command */ sync(); } while (pid != lastpid); return(rv); } int setstatus(s) register int s; { exstat = s; setval(lookup("?"), putn(s)); return(s); } /* * PATH-searching interface to execve. * If getenv("PATH") were kept up-to-date, * execvp might be used. */ char * rexecve(c, v, envp) char *c, **v, **envp; { register int i; register char *sp, *tp; int eacces = 0, asis = 0; extern int errno; sp = any('/', c)? "": path->value; asis = *sp == '\0'; while (asis || *sp != '\0') { asis = 0; tp = e.linep; for (; *sp != '\0'; tp++) if ((*tp = *sp++) == ':') { asis = *sp == '\0'; break; } if (tp != e.linep) *tp++ = '/'; for (i = 0; (*tp++ = c[i++]) != '\0';) ; execve(e.linep, v, envp); switch (errno) { case ENOEXEC: *v = e.linep; tp = *--v; *v = "/bin/sh"; execve(*v, v, envp); *v = tp; return("no Shell"); case ENOMEM: return("program too big"); case E2BIG: return("argument list too long"); case EACCES: eacces++; break; } } return(errno==ENOENT ? "not found" : "cannot execute"); } /* * Run the command produced by generator `f' * applied to stream `arg'. */ run(arg, f) struct ioarg arg; int (*f)(); { struct op *otree; struct wdblock *swdlist; struct wdblock *siolist; jmp_buf ev, rt; xint *ofail; int rv; areanum++; swdlist = wdlist; siolist = iolist; otree = outtree; ofail = failpt; rv = -1; if (newenv(setjmp(errpt = ev)) == 0) { wdlist = 0; iolist = 0; pushio(arg, f); e.iobase = e.iop; yynerrs = 0; if (setjmp(failpt = rt) == 0 && yyparse() == 0) rv = execute(outtree, NOPIPE, NOPIPE, 0); quitenv(); } wdlist = swdlist; iolist = siolist; failpt = ofail; outtree = otree; freearea(areanum--); return(rv); } /* -------- do.c -------- */ /* #include "sh.h" */ /* * built-in commands: doX */ static void rdexp(); static void badid(); static int brkcontin(); dolabel() { return(0); } dochdir(t) register struct op *t; { register char *cp, *er; if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL) er = ": no home directory"; else if(chdir(cp) < 0) er = ": bad directory"; else return(0); prs(cp != NULL? cp: "cd"); err(er); return(1); } doshift(t) register struct op *t; { register n; n = t->words[1]? getn(t->words[1]): 1; if(dolc < n) { err("nothing to shift"); return(1); } dolv[n] = dolv[0]; dolv += n; dolc -= n; setval(lookup("#"), putn(dolc)); return(0); } /* * execute login and newgrp directly */ dologin(t) struct op *t; { register char *cp; if (talking) { signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); } cp = rexecve(t->words[0], t->words, makenv(t->words)); prs(t->words[0]); prs(": "); err(cp); return(1); } doumask(t) register struct op *t; { register int i, n; register char *cp; if ((cp = t->words[1]) == NULL) { i = umask(0); umask(i); for (n=3*4; (n-=3) >= 0;) putc('0'+((i>>n)&07)); putc('\n'); } else { for (n=0; *cp>='0' && *cp<='9'; cp++) n = n*8 + (*cp-'0'); umask(n); } return(0); } doexec(t) register struct op *t; { register i; jmp_buf ex; xint *ofail; t->ioact = NULL; for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++) ; if (i == 0) return(1); execflg = 1; ofail = failpt; if (setjmp(failpt = ex) == 0) execute(t, NOPIPE, NOPIPE, FEXEC); failpt = ofail; execflg = 0; return(1); } dodot(t) struct op *t; { register i; register char *sp, *tp; char *cp; if ((cp = t->words[1]) == NULL) return(0); sp = any('/', cp)? ":": path->value; while (*sp) { tp = e.linep; while (*sp && (*tp = *sp++) != ':') tp++; if (tp != e.linep) *tp++ = '/'; for (i = 0; (*tp++ = cp[i++]) != '\0';) ; if ((i = open(e.linep, 0)) >= 0) { exstat = 0; next(remap(i)); return(exstat); } } prs(cp); err(": not found"); return(-1); } dowait(t) struct op *t; { register i; register char *cp; if ((cp = t->words[1]) != NULL) { i = getn(cp); if (i == 0) return(0); } else i = -1; if (talking) signal(SIGINT, onintr); setstatus(waitfor(i, 1)); if (talking) signal(SIGINT, SIG_IGN); return(0); } doread(t) struct op *t; { register char *cp, **wp; register nb; if (t->words[1] == NULL) { err("Usage: read name ..."); return(1); } for (wp = t->words+1; *wp; wp++) { for (cp = e.linep; cp < elinep-1; cp++) if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) || *cp == '\n' || wp[1] && any(*cp, ifs->value)) break; *cp = 0; if (nb <= 0) break; setval(lookup(*wp), e.linep); } return(nb <= 0); } doeval(t) register struct op *t; { int wdchar(); return(RUN(awordlist, t->words+1, wdchar)); } dotrap(t) register struct op *t; { register char *s; register n, i; if (t->words[1] == NULL) { for (i=0; i<NSIG; i++) if (trap[i]) { prn(i); prs(": "); prs(trap[i]); prs("\n"); } return(0); } n = getsig((s = t->words[2])!=NULL? s: t->words[1]); xfree(trap[n]); trap[n] = 0; if (s != NULL) { if ((i = strlen(s = t->words[1])) != 0) { trap[n] = strsave(s, 0); setsig(n, sig); } else setsig(n, SIG_IGN); } else setsig(n, (n == SIGINT || n == SIGQUIT) && talking? SIG_IGN: SIG_DFL); return(0); } getsig(s) char *s; { register int n; if ((n = getn(s)) < 0 || n >= NSIG) { err("trap: bad signal number"); n = 0; } return(n); } setsig(n, f) register n; int (*f)(); { if (n == 0) return; if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) { ourtrap[n] = 1; signal(n, f); } } getn(as) char *as; { register char *s; register n, m; s = as; m = 1; if (*s == '-') { m = -1; s++; } for (n = 0; digit(*s); s++) n = (n*10) + (*s-'0'); if (*s) { prs(as); err(": bad number"); } return(n*m); } dobreak(t) struct op *t; { return(brkcontin(t->words[1], 1)); } docontinue(t) struct op *t; { return(brkcontin(t->words[1], 0)); } static int brkcontin(cp, val) register char *cp; { register struct brkcon *bc; register nl; nl = cp == NULL? 1: getn(cp); if (nl <= 0) nl = 999; do { if ((bc = brklist) == NULL) break; brklist = bc->nextlev; } while (--nl); if (nl) { err("bad break/continue level"); return(1); } isbreak = val; longjmp(bc->brkpt, 1); /* NOTREACHED */ } doexit(t) struct op *t; { register char *cp; execflg = 0; if ((cp = t->words[1]) != NULL) exstat = getn(cp); leave(); } doexport(t) struct op *t; { rdexp(t->words+1, export, EXPORT); return(0); } doreadonly(t) struct op *t; { rdexp(t->words+1, ronly, RONLY); return(0); } static void rdexp(wp, f, key) register char **wp; void (*f)(); int key; { if (*wp != NULL) { for (; *wp != NULL; wp++) if (checkname(*wp)) (*f)(lookup(*wp)); else badid(*wp); } else putvlist(key, 1); } static void badid(s) register char *s; { prs(s); err(": bad identifier"); } doset(t) register struct op *t; { register struct var *vp; register char *cp; register n; if ((cp = t->words[1]) == NULL) { for (vp = vlist; vp; vp = vp->next) varput(vp->name, 1); return(0); } if (*cp == '-') { t->words++; if (*++cp == 0) flag['x'] = flag['v'] = 0; else for (; *cp; cp++) switch (*cp) { case 'e': if (!talking) flag['e']++; break; default: if (*cp>='a' && *cp<='z') flag[*cp]++; break; } setdash(); } if (t->words[1]) { t->words[0] = dolv[0]; for (n=1; t->words[n]; n++) setarea((char *)t->words[n], 0); dolc = n-1; dolv = t->words; setval(lookup("#"), putn(dolc)); setarea((char *)(dolv-1), 0); } return(0); } varput(s, out) register char *s; { if (letnum(*s)) { write(out, s, strlen(s)); write(out, "\n", 1); } } struct builtin { char *command; int (*fn)(); }; static struct builtin builtin[] = { ":", dolabel, "cd", dochdir, "shift", doshift, "exec", doexec, "wait", dowait, "read", doread, "eval", doeval, "trap", dotrap, "break", dobreak, "continue", docontinue, "exit", doexit, "export", doexport, "readonly", doreadonly, "set", doset, ".", dodot, "umask", doumask, "login", dologin, "newgrp", dologin, 0, }; int (*inbuilt(s))() register char *s; { register struct builtin *bp; for (bp = builtin; bp->command != NULL; bp++) if (strcmp(bp->command, s) == 0) return(bp->fn); return(NULL); }