124 lines · 2.4 KB · C
RawDownload
#include <fcntl.h>#include <unistd.h>#include "threadimpl.h" voidprocexec(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();} voidprocexecl(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 voidefork(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);}