aboutsummaryrefslogtreecommitdiff
path: root/sys/cmd/rc/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cmd/rc/exec.c')
-rw-r--r--sys/cmd/rc/exec.c331
1 files changed, 221 insertions, 110 deletions
diff --git a/sys/cmd/rc/exec.c b/sys/cmd/rc/exec.c
index 217f7ee..f9af866 100644
--- a/sys/cmd/rc/exec.c
+++ b/sys/cmd/rc/exec.c
@@ -10,6 +10,12 @@ struct Builtin{
void (*func)(void);
};
+struct State {
+ int async;
+};
+
+static struct State state;
+
// -----------------------------------------------------------------------
// globals
@@ -138,6 +144,14 @@ path(char *w)
static inline
void
+undoredirs(void)
+{
+ while(runner->redir.end != runner->redir.start)
+ Xpopredir();
+}
+
+static inline
+void
defaultsignal(void)
{
signal(SIGINT, SIG_DFL);
@@ -150,15 +164,50 @@ defaultsignal(void)
static inline
void
-setpid(int pid)
+setpid(Thread *job, int pid)
+{
+ job->pid = pid;
+ if(job->pgid <= 0){
+ job->pgid = pid;
+ addjob(job);
+ }
+
+ setpgid(pid, job->pgid);
+}
+
+/* fork/execute helpers */
+
+static inline
+void
+initchild(Thread *job, int fg)
{
- runner->pid = pid;
- if(runner->pgid <= 0){
- runner->pgid = runner->pid;
- addjob(runner);
+ int pid = getpid();
+ setpid(job, pid);
+
+ if(job->flag.user){
+ if(fg)
+ tcsetpgrp(0, job->pgid);
+ else
+ job->flag.user = 0;
+ defaultsignal();
}
- setpgid(pid, runner->pgid);
+ clearwait(job);
+}
+
+static inline
+void
+initparent(Thread *job, int pid, int fg)
+{
+ setpid(job, pid);
+
+ if(job->flag.user){
+ if(!fg){
+ tcsetpgrp(0, job->pgid);
+ job->flag.user = 0;
+ }
+ }
+ addwait(job, pid);
}
static
@@ -171,6 +220,7 @@ xx(void)
return;
}
+ redirect(runner->redir.end);
execute(runner->args->word, path(runner->args->word->str));
poplist();
}
@@ -180,29 +230,20 @@ int
xforkx(void)
{
int n, pid;
- char buf[ERRMAX];
switch(pid=fork()){
case -1:
+ Xerror("try again\n");
return -1;
case 0: // child
- clearwait(runner);
- runner->link = nil;
-
- if(shell.interactive){
- setpid(getpid());
- tcsetpgrp(0, runner->pgid);
- defaultsignal();
- }
+ initchild(runner, 1);
pushword("exec");
xx();
- exit(2); // NOTE: xx does not return!
- /*unreachable*/
+ exit(2); // NOTE: unreachable: xx does not return
default: // parent
- addwait(runner, pid);
- setpid(pid); // ensure the state matches for parent and child
+ initparent(runner, pid, 0);
return pid;
}
@@ -217,15 +258,14 @@ pushredir(int type, int from, int to)
r->type = type;
r->from = from;
r->to = to;
- r->link = runner->redir;
- runner->redir = r;
+ r->link = runner->redir.end, runner->redir.end = r;
}
/* byte code */
static
void
-run(Code *c, int pc, Var *local, int keeppid)
+run(Code *c, int pc, Var *local, int inherit)
{
Thread *new = emalloc(sizeof(*new));
@@ -237,10 +277,17 @@ run(Code *c, int pc, Var *local, int keeppid)
new->args = nil;
new->local = local;
- new->redir = nil;
- new->flag.i = 0;
- new->flag.eof = 0;
+ new->flag.eof = 0;
+ if(runner){
+ new->pid = runner->pid;
+ new->flag.user = runner->flag.user;
+ new->redir.end = new->redir.start = runner->redir.end;
+ }else{
+ new->pid = shell.pid;
+ new->flag.user = shell.interactive;
+ new->redir.end = new->redir.start = nil;
+ }
new->wait.status = 0;
new->wait.len = 0;
@@ -248,14 +295,14 @@ run(Code *c, int pc, Var *local, int keeppid)
new->wait.on = nil;
new->status = 0;
- if(keeppid)
- new->pgid = runner->pgid, new->pid = runner->pid;
+ if(inherit)
+ new->pgid = runner->pgid;
else
- new->pgid = new->pid = -1;
+ new->pgid = -1;
new->line = 0;
- new->link = runner;
- new->next = nil;
+ new->caller = runner;
+ new->link = nil;
runner = new;
}
@@ -354,9 +401,7 @@ xdot(void)
}
if(fd<0){
- print(shell.err, "%s: ", base);
- //setstatus("can't open");
- Xerror(".: can't open\n");
+ print(shell.err, "failed open: %s: ", base);
return;
}
/* set up for a new command loop */
@@ -432,7 +477,7 @@ xjob(void)
int i;
Thread *job;
- for(i=0, job = shell.jobs; job; job = job->next, i++)
+ for(i=0, job = shell.jobs; job; job = job->link, i++)
report(job,i);
poplist();
@@ -454,16 +499,16 @@ xfg(void)
}
i = atoi(runner->args->word->str);
- popword(); // [jobid]
+ popword(); // [pid|num]
- for(job=shell.jobs; i > 0; job=job->next, --i)
+ for(job=shell.jobs; i > 0; job=job->link, --i)
;
poplist(); // this goes here?
wakeup(job);
- job->link = runner, runner = job; // XXX: can this leave zombies?
foreground(job, 1);
+ job->caller = runner, runner = job; // XXX: can this leave zombies?
}
void
@@ -493,6 +538,7 @@ xboot(int argc, char *argv[])
bootstrap[i].i = 0;
run(bootstrap, 1, nil, 0);
+ runner->pid = runner->pgid = shell.pid;
pushlist(); // prime bootstrap argv
argv0 = strdup(argv[0]);
@@ -516,54 +562,6 @@ Xmark(void)
}
void
-Xerror(char *msg)
-{
- print(shell.err, "rc: %s", msg);
- flush(shell.err);
- while(!runner->flag.i)
- Xreturn();
-}
-
-void
-Xreturn(void)
-{
- Thread *run = runner;
-
- switch(run->wait.status){
- /*
- * If our job is still running we must:
- * 1. move program one step back to rerun Xreturn upon recall
- * 2. return to our calling thread
- * 3. don't free!
- */
- case Prun: case Pstop:
- run->code.i--;
- runner = run->link;
- return;
- /*
- * If our job has finished:
- * 1. remove from our list
- * 2. continue to clean up its memory
- */
- case Pdone:
- deljob(run);
- /* fallthrough */
- default:
- ;
- }
-
- while(run->args)
- poplist();
- freecode(run->code.exe);
- efree(run->wait.on);
-
- runner = run->link;
- efree(run);
- if(!runner)
- exit(0);
-}
-
-void
Xword(void)
{
pushword(runner->code.exe[runner->code.i++].s);
@@ -703,21 +701,18 @@ Xasync(void)
Xerror("fork failed: try again");
break;
- case 0: // child
- clearwait(runner);
+ case 0: // child in background
+ initchild(runner,0);
pushredir(Ropen, null, 0);
- if(shell.interactive){ // NOTE: this needs to read off its caller's interactivity
- setpid(getpid());
- defaultsignal();
- }
- run(runner->code.exe, runner->code.i+1, runner->local, 1);
- runner->link = nil;
+
+ run(runner->code.exe, runner->code.i+1, runner->local, 0);
+ runner->caller = nil;
+ runner->flag.user = 0;
break;
- default: // parent
+ default: // parent in foreground
+ initparent(runner,pid,1);
close(null);
- setpid(pid);
- addwait(runner, pid);
runner->code.i = runner->code.exe[runner->code.i].i; /* jump to end of async command */
/* don't wait: continue running */
@@ -727,33 +722,80 @@ Xasync(void)
void
Xsubshell(void)
{
- int pid;
+ int pid, user;
+ user = runner->flag.user;
switch(pid=fork()){
case -1:
Xerror("fork failed: try again");
break;
case 0: // child
- clearwait(runner);
- if(shell.interactive){ // NOTE: this needs to read off its caller's interactivity
- setpid(getpid());
- tcsetpgrp(0, runner->pgid);
- defaultsignal();
- }
+ initchild(runner, 1);
run(runner->code.exe, runner->code.i+1, runner->local, 1);
- runner->link = nil;
+ runner->caller = nil;
break;
default: // parent
- setpid(pid);
- addwait(runner, pid);
- waitfor(runner, pid); // wait until child finishes
+ initparent(runner, pid, 0); // relinquish control
+ waitfor(runner, pid); // wait until child finishes
+ if(user){
+ tcsetpgrp(0, shell.pid);
+ runner->flag.user = 1; // take control
+ }
+
runner->code.i = runner->code.exe[runner->code.i].i; // jump to end of subshell command and continue execution
}
}
+void
+Xpipewait(void)
+{
+ foreground(runner, 0);
+}
+
+void
+Xpipe(void)
+{
+ Thread *orig;
+ int pc, pid, lfd, rfd, pfd[2];
+
+ orig = runner;
+ pc = orig->code.i;
+ lfd = orig->code.exe[pc++].i;
+ rfd = orig->code.exe[pc++].i;
+
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe\n");
+ return;
+ }
+
+ switch(pid=fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0: // child
+ initchild(runner,1);
+
+ /* child 0 (writer) forked process */
+ run(runner->code.exe, pc+2, runner->local, 0);
+ runner->caller = nil;
+ close(pfd[0]);
+ pushredir(Ropen, pfd[1], lfd);
+ break;
+
+ default: // parent
+ initparent(runner,pid,0);
+
+ /* child 1 (reader) subprocess*/
+ run(runner->code.exe, runner->code.exe[pc].i, runner->local, 0);
+
+ close(pfd[1]);
+ pushredir(Ropen, pfd[0], rfd);
+ break;
+ }
+}
void
Xbasic(void)
@@ -783,6 +825,7 @@ Xbasic(void)
}
/* if we are here then it's an external command */
+ // TODO: check for if we will exit, don't need to fork, just exec
// run the external command
if((pid = xforkx()) < 0) {
@@ -790,9 +833,6 @@ Xbasic(void)
return;
}
- if(!shell.interactive)
- waitall(runner);
-
foreground(runner, 0); // waits for child
poplist();
}
@@ -865,7 +905,78 @@ Xbang(void)
}
void
+Xpopredir(void)
+{
+ Redir *r = runner->redir.end;
+ if(!r)
+ fatal("attempted to pop a nil redir\n");
+
+ runner->redir.end = runner->redir.end->link;
+ if(r->type==Ropen)
+ close(r->from);
+
+ efree(r);
+}
+
+void
+Xreturn(void)
+{
+ Thread *curr = runner;
+
+ switch(curr->wait.status){
+ /*
+ * If our job is still running or suspended we must:
+ * 1. move program one step back to rerun Xreturn upon recall
+ * 2. return to our calling thread
+ * 3. don't free!
+ */
+ case Prun:
+ report(curr, 0);
+ curr->flag.user = 0;
+ case Pstop:
+ curr->code.i--;
+ runner = curr->caller;
+ curr->caller = nil; // detach job
+ return;
+ /*
+ * If our job has finished:
+ * 1. remove from our list
+ * 2. continue to clean up its memory
+ */
+ case Pdone:
+ deljob(curr);
+ /* fallthrough */
+ default:
+ ;
+ }
+
+ undoredirs();
+
+ while(curr->args)
+ poplist();
+ freecode(curr->code.exe);
+ efree(curr->wait.on);
+
+ runner = curr->caller;
+ efree(curr);
+ if(!runner)
+ exit(0);
+}
+
+
+
+void
Xexit(void)
{
exit(runner->status);
}
+
+void
+Xerror(char *msg)
+{
+ print(shell.err, "rc: %s", msg);
+ flush(shell.err);
+ while(!runner->flag.user)
+ Xreturn();
+}
+