aboutsummaryrefslogtreecommitdiff
path: root/sys/cmd/rc/exec.c
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2021-10-20 13:10:54 -0700
committerNicholas Noll <nbnoll@eml.cc>2021-10-20 13:10:54 -0700
commit888679027c2e9b43d1485d970df8170ac4fda29f (patch)
treef46c257e6be1f47f4aa69d31a64643c76457a64b /sys/cmd/rc/exec.c
parent6d50d5b97d49a74a8faf587ec2bbf234626adf0c (diff)
Refactored interactivity to track with thread.
Hit a bit of a stopping point. Specifically, the way XAsync runs currently is by forking the execution context and having the child run the async code while the parent runs the remainder. The problem with this architecture is it doesn't interact well with job control. When we fork, we create a new process group. Thus the Xasync fork becomes the new leader. In short, our traversal of the parse tree as to be less "preorder" and more "in order", i.e. from the leaves up. The "left" command of the pipeline should be the "leader" of the process group.
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();
+}
+