diff options
Diffstat (limited to 'src/libthread/exec-unix.c')
-rw-r--r-- | src/libthread/exec-unix.c | 124 |
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); +} |