840 lines · 19.5 KB · C
RawDownload
/*	exec2fs - proc file system execution wrapper 	Copyright (C) 2010, IBM Corporation,  		Eric Van Hensbergen (bergevan@us.ibm.com) 	Description:		This provides an overlay file system which provides		facilities for process creation using a clone mechanism. 		Along with that, we create stdio pipes for the process		to use as I/O, and overlay those on top of the created pid		in the proc table. 	TODO:		* maybe autostart mpipe when we need it (on-demand pipes)		*/ #include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include <mp.h>#include <libsec.h>#include <stdio.h>#include <debug.h>#include "timeit.h" static char Ebadfd[] = "bad fd";static char Ebadprint[] = "bad snprint";static char Enoctl[] = "bad ctl message";static char Enopid[] =	"process not initialized";static char Eoverflow[] = "ctl buffer overflow";static char Ebadctl[] = "bad ctl message";static char Ebadfid[] = "bad fid";static char Eexec[] = "could not exec";static char Eusage[] = "exec: invalid number of arguments";static char Ectlchan[] ="problems with command channel to wrapper";static char Ectlopen[] ="problems with opening command channel to wrapper";static char Empipe[] = "problems with mpipe";static char Epid[] = "stupid pid problem";static char Esrv[] = "couldn't create srv file";static char Ewtf[] = "I have no idea what could be going wrong here"; QLock lck;int num_mnts = 0;int *mnts = nil;int iothread_id; static char defaultpath[] =	"/proc";static char defaultsrvpath[] =	"exec2fs";static char defaultcmdpath[] =	"/bin/execcmd";static char defaultsrvmpipe[] =	"mpipe";static char *procpath, *srvpath, *cmdpath, *srvmpipe, *mmount;static char *logdir = nil;static char *srvctl = nil;static Channel *iochan;static Channel *clunkchan; extern Srv fs; static voidusage(void){	fprint(2, "exec2fs [-D] [-v debuglevel] [-m mtpt] [-L logdir] [-M srvmpipe]\n");	exits("usage");} enum{	STACK = (8 * 1024),	STRMAX = 255,	Xclone = 1,}; typedef struct Exec Exec;struct Exec {	int active;	/* setup=0, executing=1 */	int pid;	/* maybe this would be sufficient for above? */	int ctlfd;	/* command and control channel */	int rctlfd;	/* actual control channel */}; static intmpipe(char *path, char *name){	int fd, ret;	char *srvpt = smprint("/srv/%s", srvmpipe);	fd = open(srvpt, ORDWR);	if(fd<0) {		DPRINT(DERR, "*ERROR*: couldn't open %s: %r\n", srvpt);		free(srvpt);		return -1;	}	free(srvpt); 	DPRINT(DFID, "mpipe: mounting name=(%s) on path=(%s) pid=(%d) fd=%d\n",	       name, path, getpid(), fd);	ret = mount(fd, -1, path, MAFTER, name);	if(ret<0)		DPRINT(DERR, "\t*ERROR*: mpipe mount failed: %r\n");	else		DPRINT(DFID, "\t\tMOUNT"); 	close(fd);	return ret;} /* call the execution wrapper */static voidcloneproc(void *arg){	Channel *pidc = arg; 	threadsetname("exec2fs-cloneproc"); 	DPRINT(DARG, "Main: srvctl=(%s)\n", srvctl);	DPRINT(DARG, "\tmmount=(%s)\n", mmount); 	DPRINT(DFID, "cloneproc pidc=(%p) (%s) (%s) (%s) (%s) (%s) (%s) (%s) (%s) (%s) (%s) (%s) (%s)\n",	       pidc, cmdpath, "execcmd", "-s", srvctl,	       "-v", smprint("0x%lx", vflag), "-L", logdir, "-m", procpath, "-M", mmount); //stamp("Before execcmd clone");	if(logdir)		procexecl(pidc, cmdpath, "execcmd", "-s", srvctl, "-v", 			  smprint("%ld", vflag), "-L", logdir, "-m", procpath, "-M", mmount, nil);	else		procexecl(pidc, cmdpath, "execcmd", "-s", srvctl, "-v", 			  smprint("0x%lx", vflag), "-m", procpath, "-M", mmount, nil);	DPRINT(DERR, "cloneproc: execcmd failed!: %r\n");// should never get here...//stamp("After execcmd clone"); //timeit_settype(TIMIT_FLOAT);//timeit_tag_dt("Before execcmd clone", "After execcmd clone", "Time for execcmd clone");//timeit_settype(TIMIT_DEFAULT); 	sendul(pidc, 0); /* failure */	threadexits(nil);} /* separated process creation to a function to help with debug */static ulongkickit(void){	Channel *pidc = chancreate(sizeof(ulong), 0);	ulong pid; //stamp("Before kickit");	DPRINT(DCUR, "kickit: forking the proc\n");	int npid = procrfork(cloneproc, (void *) pidc, STACK, RFFDG);	DPRINT(DCUR, "\tnpid=(%d)\n", npid); 	pid = recvul(pidc);	if(pid==0) {		DPRINT(DERR, "*ERROR*: Problem with cloneproc\n");	}//stamp("After kickit"); //timeit_settype(TIMIT_FLOAT);//timeit_tag_dt("Before kickit", "After kickit", "Time for kickit");//timeit_settype(TIMIT_DEFAULT); 	chanfree(pidc); 	return pid;} static voidfsopen(void *arg){	Req *r = (Req*) arg;	int n;	int p[2];	int fd;	Exec *e;	char *err;	Fid *f = r->fid;	char *fname = (char *) emalloc9p(STRMAX);	/* pathname buffer */	char *ctlbuf = (char *) emalloc9p(STRMAX);	/* error string from wrapper */ 	// FIXME: this should only be done on clone	char *srvbuf = smprint("/srv/exec2fs-%d", getpid()); /* srvctl name buffer */ ////stamp("fsopen aux");	if(f->file->aux == nil) {		DPRINT(DFID, "fsopen: f->file->aux == nil\n");////stamp("ERROR 12");		respond(r, nil);		return;	} //stamp("fsopen Xclone");	if(f->file->aux != (void*)Xclone) {			DPRINT(DCUR, "fsopen: !Xclone...\n");			DPRINT(DFID, "fsopen: f->file->aux->type != Xclone\n");//stamp("ERROR 11");			respond(r, nil);			return;	} 	/* The following section is for Xclone */ 	e = emalloc9p(sizeof(Exec));	memset(e, 0, sizeof(Exec)); // free srvctl //stamp("fsopen pipe");	pipe(p); // FIXME: moved inside of lock, but should remove the prints	DPRINT(DFID, "fsopen: srvctl=(%s) pid=(%d)\n", srvctl, getpid()); 	/* setup bootstrap ctlfd */	qlock(&lck);	srvctl = srvbuf;	fd = create(srvctl, OWRITE, 0666);	qunlock(&lck);	if(fd < 0) {		DPRINT(DFID, "fsopen failed for srvctl=(%s): %r\n", srvctl);		err = estrdup9p(Esrv);//		char *tag=smprint("ERROR 10: fsopen failed for srvctl=(%s): %r\n", srvctl);////stamp(tag);////timeit_settype(TIMIT_FLOAT);////timeit_tag_dt("fsopen pipe", tag, "Time failed open");////timeit_settype(TIMIT_DEFAULT);////stamp("ERROR 10");		goto error;	}////stamp("After create"); 	fprint(fd, "%d", p[0]);	close(fd);	close(p[0]);	e->ctlfd = p[1];//stamp("After close pipes"); 	/* ask for a new child process */	qlock(&lck);	srvctl = srvbuf; // write down the channel instead.	e->pid = (int) kickit();	qunlock(&lck);//stamp("After kickit"); 	/* cloneproc generates stdin, stdout, and stderr for the ctl	 * channel.  If any of these fail, then e->pid will be < 2, and	 * this is a hard failure, so crash instead of dieing	 * gracefully */	DPRINT(DFID, "execcmd's pid=(%d)\n", e->pid);	if(e->pid <= 2){		DPRINT(DERR, "*ERROR*: execcmd's pid=(%d) should be greater than 2\n", e->pid);		err = estrdup9p(Enopid);//stamp("ERROR 9");		goto error;	} 	/* grab actual reference to real control channel -- which	 * should be on the actual /proc so we can isolate real	 * proc/mount/name-space issues */	n = snprint(fname, STRMAX, "#p/%d/ctl", e->pid);	if(n <= 0){		DPRINT(DERR, "*ERROR*:  bad snprint #1\n");		err = estrdup9p(Ebadprint);//stamp("ERROR 8");		goto error;	}//stamp("After ctl name"); 	/* grab ahold of the ctl file */	DPRINT(DCUR, "fsopen: opening %s\n", fname);	if((e->rctlfd = open(fname, OWRITE)) < 0) {		DPRINT(DERR, "\n*ERROR*: opening %s failed: %r\n", fname);		err = estrdup9p(Enoctl);//stamp("ERROR 7");		goto error;	}	DPRINT(DFID, "\tctlfd=(%d)\n", e->rctlfd);//stamp("After ctl creation"); //  FIXME: this gets overwritten and is not used....//	n = snprint(fname, STRMAX, "%s/%d", mmount, e->pid);//	DPRINT(DCUR, "fsopen: mounting on %s\n", fname);//	if(n <= 0){//		DPRINT(DERR, "*ERROR*: bad snprint #2\n");//		err = estrdup9p(Ebadprint);//		goto error;	//	}////stamp("After mount name"); 	n = snprint(fname, STRMAX, "%d", e->pid);	if(n <= 0){		DPRINT(DERR, "*ERROR*: bad snprint #3\n");		err = estrdup9p(Ebadprint);//stamp("ERROR 7");		goto error;		}//stamp("After mount name"); 	closefile(createfile(fs.tree->root, fname, "exec2fs", DMDIR|0777, nil));	DPRINT(DCUR, "\tcreated submount point %s\n", fname);//stamp("After createfile"); 	n = snprint(fname, STRMAX, "%s/%d", mmount, e->pid);	if(n <= 0){		DPRINT(DERR, "*ERROR*: bad snprint #4\n");		err = estrdup9p(Ebadprint);//stamp("ERROR 6");		goto error;		} 	/* mount the mpipes */	n = mpipe(fname, "stdin");	if(n <= 0){		DPRINT(DERR, "*ERROR*: bad mpipe for stdin\n");		err = estrdup9p(Empipe);//stamp("ERROR 6");		goto error;		}//stamp("After stdin");	n = mpipe(fname, "stdout");	if(n <= 0){		DPRINT(DERR, "*ERROR*: bad mpipe for stdout\n");		err = estrdup9p(Empipe);//stamp("ERROR 5");		goto error;		}//stamp("After stdout");	n = mpipe(fname, "stderr");	if(n <= 0){		DPRINT(DERR, "*ERROR*: bad mpipe for stderr\n");		err = estrdup9p(Empipe);//stamp("ERROR 4");		goto error;		}//stamp("After stderr"); 	// FIXME: hack to keep track of mnts	qlock(&lck);	mnts = erealloc9p(mnts, (num_mnts+1)*sizeof(int));	mnts[num_mnts++] = e->pid;	qunlock(&lck);	DPRINT(DCUR, "fsopen: mounted new pipe num_mnts=(%d)\n", num_mnts); 	/* handshake to execcmd */	n = write(e->ctlfd, "1", 1);	if(n < 0) {		DPRINT(DERR, "fsopen: *ERROR*: execcmd write failed\n");		err = estrdup9p(Ectlchan);//stamp("ERROR 3");		goto error;		}//stamp("After write 1"); 	/* check for partial success: execcmd has opened stdio */	n = read(e->ctlfd, ctlbuf, STRMAX);	if(n < 0) {		DPRINT(DERR, "fsopen: *ERROR*: execcmd read failed\n");		err = estrdup9p(Ectlchan);//stamp("ERROR 2");		goto error;		}	if(n != 1) {		DPRINT(DERR, "fsopen: *ERROR*: execcmd n != 1\n");		ctlbuf[n] = 0;		err = estrdup9p(ctlbuf);//stamp("ERROR 1");		goto error;	}//stamp("After read 1");//timeit_settype(TIMIT_FLOAT);//timeit_tag_dt("fsopen pipe", "After close pipes", "Time for fsopen pipes");//timeit_tag_dt("After close pipes", "After kickit", "Time for fsopen kickit");//timeit_tag_dt("After kickit", "After ctl creation", "Time for fsopen ctl");//timeit_tag_dt("After ctl creation", "After createfile", "Time for createfile");//timeit_tag_dt("After createfile", "After stderr", "Time for std");//timeit_tag_dt("After stderr", "After read 1", "Time for write/read");//timeit_tag_dt("fsopen pipe", "After read 1", "Time for sess");//timeit_settype(TIMIT_DEFAULT); 	f->aux = e;	DPRINT(DCUR, "fsopen: sucseeded - setting f->aux = e (%p)\n", f->aux);	DPRINT(DCUR, "\tf->fid=%d\n", f->fid);	free(fname);	free(ctlbuf);	respond(r, nil);	return; error:	DPRINT(DERR, "fsopen: *ERROR*: %s\n", err);	free(fname);	free(ctlbuf);	/* free channel */	free(e);	f->aux = nil;	respond(r, err);	free(err);} static voidfswrite(Req *r){	Fid *f = r->fid;	Exec *e = f->aux;	char ctlbuf[256];	int ret; 	if(e == 0) {		respond(r, Ebadfid);	} 	if(e->ctlfd == -1) {		DPRINT(DERR, "*ERROR*(fswrite): %s for ctl file: %r\n", Ebadfd);		respond(r, Ebadfd);	} 	DPRINT(DFID, "fswrite: ctlfd=(%d) count=(%d) pid=(%d)\n",	       e->ctlfd, r->ifcall.count, getpid()); 	ret = write(e->ctlfd, r->ifcall.data, r->ifcall.count);	if(ret < 0) {		DPRINT(DFID, "*ERROR*(fswrite): write pid=(%d): %r\n", getpid());		responderror(r);		return;	} else if(e->active) {	/* program already executing */		goto success;	} 	/* not active yet: read to get response */ 	/* this assumes that the only thing expected to be written is	 * the exec cmd if the session is not yet active. */	ret = read(e->ctlfd, ctlbuf, STRMAX);	if(ret < 0) {		DPRINT(DFID, "*ERROR*(fswrite): read pid=(%d): %r\n", getpid());		responderror(r);		return;	}		if(ret == 0) {		/* other side exec'd - go active */		close(e->ctlfd);		/* replace our channel with the real control channel */		e->ctlfd = e->rctlfd;		e->active++;		DPRINT(DFID, "fswrite: go active=(%d)\n", e->active); 		goto success;	} 	/* convention: a single byte is sucess, multibyte is an error message */	if(ret != 1) {		DPRINT(DFID, "fswrite: single byte success\n");		ctlbuf[ret] = 0;		respond(r, ctlbuf);		return;	}success:	DPRINT(DFID, "fswrite: generall success\n");	r->ofcall.count = r->ifcall.count;	respond(r, nil);	return;} static voidfsread(Req *r){	Fid *f = r->fid;	Exec *e = f->aux; 	DPRINT(DFID, "fsread: ctlfd=(%d) count=(%d) pid=(%d)\n",	       e->ctlfd, r->ifcall.count, getpid()); 	if(e->pid == 0) {		DPRINT(DERR, "*ERROR*(fsread): no pid\n");		respond(r, Enopid);	} else {		DPRINT(DFID, "fsread: reading...\n");		char buf[16];		snprint(buf, 16, "%d", e->pid);		readstr(r, buf);		respond(r, nil);	}	} static intflushmp(char *src){	int fd = open(src, OWRITE);	int n; 	char hdr[255];	ulong tag = ~0; /* header byte is at offset ~0 */	char pkttype='.'; 	if(fd < 0) {		DPRINT(DERR, "flushmp: open failed: %r\n");		return fd;

Lines 1–500 of 840.