125 lines
2.4 KiB
C
125 lines
2.4 KiB
C
|
|
#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);
|
||
|
|
}
|