forsyth20121205-174063a80cc13 years agoHistory
683 lines · 11.7 KB · C
RawDownload
#include	"dat.h"#include	"fns.h"#include	"error.h" enum{	Qtopdir,	/* top level directory */	Qcmd,	Qclonus,	Qconvdir,	Qconvbase,	Qdata = Qconvbase,	Qstderr,	Qctl,	Qstatus,	Qwait, 	Debug=0	/* to help debug os.c */};#define TYPE(x) 	((ulong)(x).path & 0xf)#define CONV(x) 	(((ulong)(x).path >> 4)&0xfff)#define QID(c, y) 	(((c)<<4) | (y)) typedef struct Conv	Conv;struct Conv{	int	x;	int	inuse;	int	fd[3];	/* stdin, stdout, and stderr */	int	count[3];	/* number of readers on stdin/stdout/stderr */	int	perm;	char*	owner;	char*	state;	Cmdbuf*	cmd;	char*	dir;	QLock	l;	/* protects state changes */	Queue*	waitq;	void*	child;	char*	error;	/* on start up */	int	nice;	short	killonclose;	short	killed;	Rendez	startr;}; static struct{	QLock	l;	int	nc;	int	maxconv;	Conv**	conv;} cmd; static	Conv*	cmdclone(char*);static	void	cmdproc(void*); static intcmd3gen(Chan *c, int i, Dir *dp){	Qid q;	Conv *cv; 	cv = cmd.conv[CONV(c->qid)];	switch(i){	default:		return -1;	case Qdata:		mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE);		devdir(c, q, "data", 0, cv->owner, cv->perm, dp);		return 1;	case Qstderr:		mkqid(&q, QID(CONV(c->qid), Qstderr), 0, QTFILE);		devdir(c, q, "stderr", 0, cv->owner, 0444, dp);		return 1;	case Qctl:		mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE);		devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);		return 1;	case Qstatus:		mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE);		devdir(c, q, "status", 0, cv->owner, 0444, dp);		return 1;	case Qwait:		mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE);		devdir(c, q, "wait", 0, cv->owner, 0444, dp);		return 1;	}} static intcmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp){	Qid q;	Conv *cv; 	USED(name);	USED(nd);	USED(d); 	if(s == DEVDOTDOT){		switch(TYPE(c->qid)){		case Qtopdir:		case Qcmd:			mkqid(&q, QID(0, Qtopdir), 0, QTDIR);			devdir(c, q, "#C", 0, eve, DMDIR|0555, dp);			break;		case Qconvdir:			mkqid(&q, QID(0, Qcmd), 0, QTDIR);			devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp);			break;		default:			panic("cmdgen %llux", c->qid.path);		}		return 1;	} 	switch(TYPE(c->qid)) {	case Qtopdir:		if(s >= 1)			return -1;		mkqid(&q, QID(0, Qcmd), 0, QTDIR);		devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp);		return 1;	case Qcmd:		if(s < cmd.nc) {			cv = cmd.conv[s];			mkqid(&q, QID(s, Qconvdir), 0, QTDIR);			sprint(up->genbuf, "%d", s);			devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp);			return 1;		}		s -= cmd.nc;		if(s == 0){			mkqid(&q, QID(0, Qclonus), 0, QTFILE);			devdir(c, q, "clone", 0, "cmd", 0666, dp);			return 1;		}		return -1;	case Qclonus:		if(s == 0){			mkqid(&q, QID(0, Qclonus), 0, QTFILE);			devdir(c, q, "clone", 0, "cmd", 0666, dp);			return 1;		}		return -1;	case Qconvdir:		return cmd3gen(c, Qconvbase+s, dp);	case Qdata:	case Qstderr:	case Qctl:	case Qstatus:	case Qwait:		return cmd3gen(c, TYPE(c->qid), dp);	}	return -1;} static voidcmdinit(void){	cmd.maxconv = 1000;	cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1);	/* cmd.conv is checked by cmdattach, below */} static Chan *cmdattach(char *spec){	Chan *c; 	if(cmd.conv == nil)		error(Enomem);	c = devattach('C', spec);	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);	return c;} static Walkqid*cmdwalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, 0, 0, cmdgen);} static intcmdstat(Chan *c, uchar *db, int n){	return devstat(c, db, n, 0, 0, cmdgen);} static Chan *cmdopen(Chan *c, int omode){	int perm;	Conv *cv;	char *user; 	perm = 0;	omode = openmode(omode);	switch(omode) {	case OREAD:		perm = 4;		break;	case OWRITE:		perm = 2;		break;	case ORDWR:		perm = 6;		break;	} 	switch(TYPE(c->qid)) {	default:		break;	case Qtopdir:	case Qcmd:	case Qconvdir:	case Qstatus:		if(omode != OREAD)			error(Eperm);		break;	case Qclonus:		qlock(&cmd.l);		if(waserror()){			qunlock(&cmd.l);			nexterror();		}		cv = cmdclone(up->env->user);		poperror();		qunlock(&cmd.l);		if(cv == 0)			error(Enodev);		mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE);		break;	case Qdata:	case Qstderr:	case Qctl:	case Qwait:		qlock(&cmd.l);		cv = cmd.conv[CONV(c->qid)];		qlock(&cv->l);		if(waserror()){			qunlock(&cv->l);			qunlock(&cmd.l);			nexterror();		}		user = up->env->user;		if((perm & (cv->perm>>6)) != perm) {			if(strcmp(user, cv->owner) != 0 ||		 	  (perm & cv->perm) != perm)				error(Eperm);		}		switch(TYPE(c->qid)){		case Qdata:			if(omode == OWRITE || omode == ORDWR)				cv->count[0]++;			if(omode == OREAD || omode == ORDWR)				cv->count[1]++;			break;		case Qstderr:			if(omode != OREAD)				error(Eperm);			cv->count[2]++;			break;		case Qwait:			if(cv->waitq == nil)				cv->waitq = qopen(1024, Qmsg, nil, 0);			break;		}		cv->inuse++;		if(cv->inuse == 1) {			cv->state = "Open";			kstrdup(&cv->owner, user);			cv->perm = 0660;			cv->nice = 0;		}		poperror();		qunlock(&cv->l);		qunlock(&cmd.l);		break;	}	c->mode = omode;	c->flag |= COPEN;	c->offset = 0;	return c;} static voidcloseconv(Conv *c){	kstrdup(&c->owner, "cmd");	kstrdup(&c->dir, rootdir);	c->perm = 0666;	c->state = "Closed";	c->killonclose = 0;	c->killed = 0;	c->nice = 0;	free(c->cmd);	c->cmd = nil;	if(c->waitq != nil){		qfree(c->waitq);		c->waitq = nil;	}	free(c->error);	c->error = nil;} static voidcmdfdclose(Conv *c, int fd){	if(--c->count[fd] == 0 && c->fd[fd] != -1){		close(c->fd[fd]);		c->fd[fd] = -1;	}} static voidcmdclose(Chan *c){	Conv *cc;	int r; 	if((c->flag & COPEN) == 0)		return; 	switch(TYPE(c->qid)) {	case Qctl:	case Qdata:	case Qstderr:	case Qwait:		cc = cmd.conv[CONV(c->qid)];		qlock(&cc->l);		if(TYPE(c->qid) == Qdata){			if(c->mode == OWRITE || c->mode == ORDWR)				cmdfdclose(cc, 0);			if(c->mode == OREAD || c->mode == ORDWR)				cmdfdclose(cc, 1);		}else if(TYPE(c->qid) == Qstderr)			cmdfdclose(cc, 2); 		r = --cc->inuse;		if(cc->child != nil){			if(!cc->killed)			if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){				oscmdkill(cc->child);				cc->killed = 1;			}		}else if(r == 0)			closeconv(cc); 		qunlock(&cc->l);		break;	}} static longcmdread(Chan *ch, void *a, long n, vlong offset){	Conv *c;	char *p, *cmds;	int fd; 	USED(offset); 	p = a;	switch(TYPE(ch->qid)) {	default:		error(Eperm);	case Qcmd:	case Qtopdir:	case Qconvdir:		return devdirread(ch, a, n, 0, 0, cmdgen);	case Qctl:		sprint(up->genbuf, "%ld", CONV(ch->qid));		return readstr(offset, p, n, up->genbuf);	case Qstatus:		c = cmd.conv[CONV(ch->qid)];		cmds = "";		if(c->cmd != nil)			cmds = c->cmd->f[1];		snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n",			c->x, c->inuse, c->state, c->dir, cmds);		return readstr(offset, p, n, up->genbuf);	case Qdata:	case Qstderr:		fd = 1;		if(TYPE(ch->qid) == Qstderr)			fd = 2;		c = cmd.conv[CONV(ch->qid)];		qlock(&c->l);		if(c->fd[fd] == -1){			qunlock(&c->l);			return 0;		}		qunlock(&c->l);		osenter();		n = read(c->fd[fd], a, n);		osleave();		if(n < 0)			oserror();		return n;	case Qwait:		c = cmd.conv[CONV(ch->qid)];		return qread(c->waitq, a, n);	}} static intcmdstarted(void *a){	Conv *c; 	c = a;	return c->child != nil || c->error != nil || strcmp(c->state, "Execute") != 0;} enum{	CMdir,	CMexec,	CMkill,	CMnice,	CMkillonclose}; staticCmdtab cmdtab[] = {	CMdir,	"dir",	2,	CMexec,	"exec",	0,	CMkill,	"kill",	1,	CMnice,	"nice",	0,	CMkillonclose, "killonclose", 0,}; static longcmdwrite(Chan *ch, void *a, long n, vlong offset){	int i, r;	Conv *c;	Cmdbuf *cb;	Cmdtab *ct; 	USED(offset); 	switch(TYPE(ch->qid)) {	default:		error(Eperm);	case Qctl:		c = cmd.conv[CONV(ch->qid)];		cb = parsecmd(a, n);		if(waserror()){			free(cb);			nexterror();		}		ct = lookupcmd(cb, cmdtab, nelem(cmdtab));		switch(ct->index){		case CMdir:			kstrdup(&c->dir, cb->f[1]);			break;		case CMexec:			poperror();	/* cb */			qlock(&c->l);			if(waserror()){				qunlock(&c->l);				free(cb);				nexterror();			}			if(c->child != nil || c->cmd != nil)				error(Einuse);			for(i = 0; i < nelem(c->fd); i++)				if(c->fd[i] != -1)					error(Einuse);			if(cb->nf < 1)				error(Etoosmall);			kproc("cmdproc", cmdproc, c, 0);	/* cmdproc held back until unlock below */			free(c->cmd);			c->cmd = cb;	/* don't free cb */			c->state = "Execute";			poperror();			qunlock(&c->l);			while(waserror())				;			Sleep(&c->startr, cmdstarted, c);			poperror();			if(c->error)				error(c->error);			return n;	/* avoid free(cb) below */		case CMkill:			qlock(&c->l);			if(waserror()){				qunlock(&c->l);				nexterror();			}			if(c->child == nil)				error("not started");			if(oscmdkill(c->child) < 0)				oserror();			poperror();			qunlock(&c->l);			break;		case CMnice:

Lines 1–500 of 683.