aboutsummaryrefslogtreecommitdiff
path: root/src/libthread/exec-unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libthread/exec-unix.c')
-rw-r--r--src/libthread/exec-unix.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/libthread/exec-unix.c b/src/libthread/exec-unix.c
new file mode 100644
index 00000000..5a37e34c
--- /dev/null
+++ b/src/libthread/exec-unix.c
@@ -0,0 +1,124 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include "threadimpl.h"
+
+void
+procexec(Channel *pidc, char *prog, char *args[])
+{
+ int n;
+ Proc *p;
+ Thread *t;
+
+ _threaddebug(DBGEXEC, "procexec %s", prog);
+ /* must be only thread in proc */
+ p = _threadgetproc();
+ t = p->thread;
+ if(p->threads.head != t || p->threads.head->nextt != nil){
+ werrstr("not only thread in proc");
+ Bad:
+ if(pidc)
+ sendul(pidc, ~0);
+ return;
+ }
+
+ /*
+ * We want procexec to behave like exec; if exec succeeds,
+ * never return, and if it fails, return with errstr set.
+ * Unfortunately, the exec happens in another proc since
+ * we have to wait for the exec'ed process to finish.
+ * To provide the semantics, we open a pipe with the
+ * write end close-on-exec and hand it to the proc that
+ * is doing the exec. If the exec succeeds, the pipe will
+ * close so that our read below fails. If the exec fails,
+ * then the proc doing the exec sends the errstr down the
+ * pipe to us.
+ */
+ if(pipe(p->exec.fd) < 0)
+ goto Bad;
+ if(fcntl(p->exec.fd[1], F_SETFD, 1) < 0)
+ goto Bad;
+
+ /* exec in parallel via the scheduler */
+ assert(p->needexec==0);
+ p->exec.prog = prog;
+ p->exec.args = args;
+ p->needexec = 1;
+ _sched();
+
+ close(p->exec.fd[1]);
+ if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exec failed */
+ p->exitstr[n] = '\0';
+ errstr(p->exitstr, ERRMAX);
+ close(p->exec.fd[0]);
+ goto Bad;
+ }
+ close(p->exec.fd[0]);
+
+ if(pidc)
+ sendul(pidc, t->ret);
+
+ /* wait for exec'ed program, then exit */
+ _schedexecwait();
+}
+
+void
+procexecl(Channel *pidc, char *f, ...)
+{
+ procexec(pidc, f, &f+1);
+}
+
+void
+_schedexecwait(void)
+{
+ int pid;
+ Channel *c;
+ Proc *p;
+ Thread *t;
+ Waitmsg *w;
+
+ p = _threadgetproc();
+ t = p->thread;
+ pid = t->ret;
+ _threaddebug(DBGEXEC, "_schedexecwait %d", t->ret);
+
+ for(;;){
+ w = wait();
+ if(w == nil)
+ break;
+ if(w->pid == pid)
+ break;
+ free(w);
+ }
+ if(w != nil){
+ if((c = _threadwaitchan) != nil)
+ sendp(c, w);
+ else
+ free(w);
+ }
+ threadexits("procexec");
+}
+
+static void
+efork(void *ve)
+{
+ char buf[ERRMAX];
+ Execargs *e;
+
+ e = ve;
+ _threaddebug(DBGEXEC, "_schedexec %s", e->prog);
+ close(e->fd[0]);
+ execv(e->prog, e->args);
+ _threaddebug(DBGEXEC, "_schedexec failed: %r");
+ rerrstr(buf, sizeof buf);
+ if(buf[0]=='\0')
+ strcpy(buf, "exec failed");
+ write(e->fd[1], buf, strlen(buf));
+ close(e->fd[1]);
+ _exits(buf);
+}
+
+int
+_schedexec(Execargs *e)
+{
+ return ffork(RFFDG|RFPROC|RFMEM, efork, e);
+}