plan9fox/sys/src/cmd/rc/simple.c
cinap_lenrek 258fe87faf rc: terminate rc when exec fails, cleanup
The execexec() function should never return, as it irreversably changes
the filedescriptor table for the new program. This means rc's internal
filedesciptors for reading the script get implicitely closed and we cannot
continue the rc interpreter when Execute() fails. So Execute() now sets the
error status, and execexec() runs Xexit() in case Execute() returns.
2016-02-22 22:25:21 +01:00

518 lines
9 KiB
C

/*
* Maybe `simple' is a misnomer.
*/
#include "rc.h"
#include "getflags.h"
#include "exec.h"
#include "io.h"
#include "fns.h"
/*
* Search through the following code to see if we're just going to exit.
*/
int
exitnext(void){
union code *c=&runq->code[runq->pc];
while(c->f==Xpopredir) c++;
return c->f==Xexit;
}
void
Xsimple(void)
{
word *a;
thread *p = runq;
var *v;
struct builtin *bp;
int pid;
globlist();
a = runq->argv->words;
if(a==0){
Xerror1("empty argument list");
return;
}
if(flag['x'])
pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */
v = gvlook(a->word);
if(v->fn)
execfunc(v);
else{
if(strcmp(a->word, "builtin")==0){
if(count(a)==1){
pfmt(err, "builtin: empty argument list\n");
setstatus("empty arg list");
poplist();
return;
}
a = a->next;
popword();
}
for(bp = Builtin;bp->name;bp++)
if(strcmp(a->word, bp->name)==0){
(*bp->fnc)();
return;
}
if(exitnext()){
/* fork and wait is redundant */
pushword("exec");
execexec();
}
else{
flush(err);
Updenv(); /* necessary so changes don't go out again */
if((pid = execforkexec()) < 0){
Xerror("try again");
return;
}
/* interrupts don't get us out */
poplist();
while(Waitfor(pid, 1) < 0)
;
}
}
}
struct word nullpath = { "", 0};
void
doredir(redir *rp)
{
if(rp){
doredir(rp->next);
switch(rp->type){
case ROPEN:
if(rp->from!=rp->to){
Dup(rp->from, rp->to);
close(rp->from);
}
break;
case RDUP:
Dup(rp->from, rp->to);
break;
case RCLOSE:
close(rp->from);
break;
}
}
}
word*
searchpath(char *w)
{
word *path;
if(strncmp(w, "/", 1)==0
|| strncmp(w, "#", 1)==0
|| strncmp(w, "./", 2)==0
|| strncmp(w, "../", 3)==0
|| (path = vlook("path")->val)==0)
path=&nullpath;
return path;
}
void
execexec(void)
{
popword(); /* "exec" */
if(runq->argv->words==0){
Xerror1("empty argument list");
return;
}
doredir(runq->redir);
Execute(runq->argv->words, searchpath(runq->argv->words->word));
poplist();
Xexit();
}
void
execfunc(var *func)
{
word *starval;
popword();
starval = runq->argv->words;
runq->argv->words = 0;
poplist();
start(func->fn, func->pc, runq->local);
runq->local = newvar("*", runq->local);
runq->local->val = starval;
runq->local->changed = 1;
}
int
dochdir(char *word)
{
/* report to /dev/wdir if it exists and we're interactive */
static int wdirfd = -2;
if(chdir(word)<0) return -1;
if(flag['i']!=0){
if(wdirfd==-2) /* try only once */
wdirfd = open("/dev/wdir", OWRITE|OCEXEC);
if(wdirfd>=0)
write(wdirfd, word, strlen(word));
}
return 1;
}
void
execcd(void)
{
word *a = runq->argv->words;
word *cdpath;
char *dir;
setstatus("can't cd");
cdpath = vlook("cdpath")->val;
switch(count(a)){
default:
pfmt(err, "Usage: cd [directory]\n");
break;
case 2:
if(a->next->word[0]=='/' || cdpath==0)
cdpath = &nullpath;
for(; cdpath; cdpath = cdpath->next){
if(cdpath->word[0] != '\0')
dir = smprint("%s/%s", cdpath->word,
a->next->word);
else
dir = estrdup(a->next->word);
if(dochdir(dir) >= 0){
if(cdpath->word[0] != '\0' &&
strcmp(cdpath->word, ".") != 0)
pfmt(err, "%s\n", dir);
free(dir);
setstatus("");
break;
}
free(dir);
}
if(cdpath==0)
pfmt(err, "Can't cd %s: %r\n", a->next->word);
break;
case 1:
a = vlook("home")->val;
if(count(a)>=1){
if(dochdir(a->word)>=0)
setstatus("");
else
pfmt(err, "Can't cd %s: %r\n", a->word);
}
else
pfmt(err, "Can't cd -- $home empty\n");
break;
}
poplist();
}
void
execexit(void)
{
switch(count(runq->argv->words)){
default:
pfmt(err, "Usage: exit [status]\nExiting anyway\n");
case 2:
setstatus(runq->argv->words->next->word);
case 1: Xexit();
}
}
void
execshift(void)
{
int n;
word *a;
var *star;
switch(count(runq->argv->words)){
default:
pfmt(err, "Usage: shift [n]\n");
setstatus("shift usage");
poplist();
return;
case 2:
n = atoi(runq->argv->words->next->word);
break;
case 1:
n = 1;
break;
}
star = vlook("*");
for(;n && star->val;--n){
a = star->val->next;
efree(star->val->word);
efree((char *)star->val);
star->val = a;
star->changed = 1;
}
setstatus("");
poplist();
}
int
octal(char *s)
{
int n = 0;
while(*s==' ' || *s=='\t' || *s=='\n') s++;
while('0'<=*s && *s<='7') n = n*8+*s++-'0';
return n;
}
int
mapfd(int fd)
{
redir *rp;
for(rp = runq->redir;rp;rp = rp->next){
switch(rp->type){
case RCLOSE:
if(rp->from==fd)
fd=-1;
break;
case RDUP:
case ROPEN:
if(rp->to==fd)
fd = rp->from;
break;
}
}
return fd;
}
union code rdcmds[4];
void
execcmds(io *f)
{
static int first = 1;
if(first){
rdcmds[0].i = 1;
rdcmds[1].f = Xrdcmds;
rdcmds[2].f = Xreturn;
first = 0;
}
start(rdcmds, 1, runq->local);
runq->cmdfd = f;
runq->iflast = 0;
}
void
execeval(void)
{
char *cmdline, *s, *t;
int len = 0;
word *ap;
if(count(runq->argv->words)<=1){
Xerror1("Usage: eval cmd ...");
return;
}
eflagok = 1;
for(ap = runq->argv->words->next;ap;ap = ap->next)
len+=1+strlen(ap->word);
cmdline = emalloc(len);
s = cmdline;
for(ap = runq->argv->words->next;ap;ap = ap->next){
for(t = ap->word;*t;) *s++=*t++;
*s++=' ';
}
s[-1]='\n';
poplist();
execcmds(opencore(cmdline, len));
efree(cmdline);
}
union code dotcmds[14];
void
execdot(void)
{
int iflag = 0;
int fd;
list *av;
thread *p = runq;
char *zero, *file;
word *path;
static int first = 1;
if(first){
dotcmds[0].i = 1;
dotcmds[1].f = Xmark;
dotcmds[2].f = Xword;
dotcmds[3].s="0";
dotcmds[4].f = Xlocal;
dotcmds[5].f = Xmark;
dotcmds[6].f = Xword;
dotcmds[7].s="*";
dotcmds[8].f = Xlocal;
dotcmds[9].f = Xrdcmds;
dotcmds[10].f = Xunlocal;
dotcmds[11].f = Xunlocal;
dotcmds[12].f = Xreturn;
first = 0;
}
else
eflagok = 1;
popword();
if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
iflag = 1;
popword();
}
/* get input file */
if(p->argv->words==0){
Xerror1("Usage: . [-i] file [arg ...]");
return;
}
zero = estrdup(p->argv->words->word);
popword();
fd = -1;
for(path = searchpath(zero); path; path = path->next){
if(path->word[0] != '\0')
file = smprint("%s/%s", path->word, zero);
else
file = estrdup(zero);
fd = open(file, 0);
free(file);
if(fd >= 0)
break;
if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */
fd = Dup1(0);
if(fd>=0)
break;
}
}
if(fd<0){
pfmt(err, "%s: ", zero);
setstatus("can't open");
Xerror(".: can't open");
return;
}
/* set up for a new command loop */
start(dotcmds, 1, (struct var *)0);
pushredir(RCLOSE, fd, 0);
runq->cmdfile = zero;
runq->cmdfd = openfd(fd);
runq->iflag = iflag;
runq->iflast = 0;
/* push $* value */
pushlist();
runq->argv->words = p->argv->words;
/* free caller's copy of $* */
av = p->argv;
p->argv = av->next;
efree((char *)av);
/* push $0 value */
pushlist();
pushword(zero);
ndot++;
}
void
execflag(void)
{
char *letter, *val;
switch(count(runq->argv->words)){
case 2:
setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
break;
case 3:
letter = runq->argv->words->next->word;
val = runq->argv->words->next->next->word;
if(strlen(letter)==1){
if(strcmp(val, "+")==0){
flag[(uchar)letter[0]] = flagset;
break;
}
if(strcmp(val, "-")==0){
flag[(uchar)letter[0]] = 0;
break;
}
}
default:
Xerror1("Usage: flag [letter] [+-]");
return;
}
poplist();
}
void
execwhatis(void){ /* mildly wrong -- should fork before writing */
word *a, *b, *path;
var *v;
struct builtin *bp;
char *file;
struct io out[1];
int found, sep;
a = runq->argv->words->next;
if(a==0){
Xerror1("Usage: whatis name ...");
return;
}
setstatus("");
out->fd = mapfd(1);
out->bufp = out->buf;
out->ebuf = &out->buf[NBUF];
out->strp = 0;
for(;a;a = a->next){
v = vlook(a->word);
if(v->val){
pfmt(out, "%s=", a->word);
if(v->val->next==0)
pfmt(out, "%q\n", v->val->word);
else{
sep='(';
for(b = v->val;b && b->word;b = b->next){
pfmt(out, "%c%q", sep, b->word);
sep=' ';
}
pfmt(out, ")\n");
}
found = 1;
}
else
found = 0;
v = gvlook(a->word);
if(v->fn)
pfmt(out, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
else{
for(bp = Builtin;bp->name;bp++)
if(strcmp(a->word, bp->name)==0){
pfmt(out, "builtin %s\n", a->word);
break;
}
if(!bp->name){
for(path = searchpath(a->word); path;
path = path->next){
if(path->word[0] != '\0')
file = smprint("%s/%s",
path->word, a->word);
else
file = estrdup(a->word);
if(Executable(file)){
pfmt(out, "%s\n", file);
free(file);
break;
}
free(file);
}
if(!path && !found){
pfmt(err, "%s: not found\n", a->word);
setstatus("not found");
}
}
}
}
poplist();
flush(err);
}
void
execwait(void)
{
switch(count(runq->argv->words)){
default:
Xerror1("Usage: wait [pid]");
return;
case 2:
Waitfor(atoi(runq->argv->words->next->word), 0);
break;
case 1:
Waitfor(-1, 0);
break;
}
poplist();
}