xref: /plan9-contrib/sys/src/9/port/devpipe.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include	"u.h"
23e12c5d1SDavid du Colombier #include	"../port/lib.h"
33e12c5d1SDavid du Colombier #include	"mem.h"
43e12c5d1SDavid du Colombier #include	"dat.h"
53e12c5d1SDavid du Colombier #include	"fns.h"
63e12c5d1SDavid du Colombier #include	"../port/error.h"
73e12c5d1SDavid du Colombier 
83e12c5d1SDavid du Colombier #include	"devtab.h"
93e12c5d1SDavid du Colombier 
103e12c5d1SDavid du Colombier typedef struct Pipe	Pipe;
113e12c5d1SDavid du Colombier struct Pipe
123e12c5d1SDavid du Colombier {
133e12c5d1SDavid du Colombier 	Ref;
143e12c5d1SDavid du Colombier 	QLock;
153e12c5d1SDavid du Colombier 	Pipe	*next;
163e12c5d1SDavid du Colombier 	ulong	path;
173e12c5d1SDavid du Colombier };
183e12c5d1SDavid du Colombier 
193e12c5d1SDavid du Colombier struct
203e12c5d1SDavid du Colombier {
213e12c5d1SDavid du Colombier 	Lock;
223e12c5d1SDavid du Colombier 	Pipe	*pipe;
233e12c5d1SDavid du Colombier 	ulong	path;
243e12c5d1SDavid du Colombier } pipealloc;
253e12c5d1SDavid du Colombier 
263e12c5d1SDavid du Colombier static Pipe *getpipe(ulong);
273e12c5d1SDavid du Colombier static void pipeiput(Queue*, Block*);
283e12c5d1SDavid du Colombier static void pipeoput(Queue*, Block*);
293e12c5d1SDavid du Colombier static void pipestclose(Queue *);
303e12c5d1SDavid du Colombier 
313e12c5d1SDavid du Colombier Qinfo pipeinfo =
323e12c5d1SDavid du Colombier {
333e12c5d1SDavid du Colombier 	pipeiput,
343e12c5d1SDavid du Colombier 	pipeoput,
353e12c5d1SDavid du Colombier 	0,
363e12c5d1SDavid du Colombier 	pipestclose,
373e12c5d1SDavid du Colombier 	"pipe"
383e12c5d1SDavid du Colombier };
393e12c5d1SDavid du Colombier 
403e12c5d1SDavid du Colombier Dirtab pipedir[] =
413e12c5d1SDavid du Colombier {
423e12c5d1SDavid du Colombier 	"data",		{Sdataqid},	0,			0600,
433e12c5d1SDavid du Colombier 	"ctl",		{Sctlqid},	0,			0600,
443e12c5d1SDavid du Colombier 	"data1",	{Sdataqid},	0,			0600,
453e12c5d1SDavid du Colombier 	"ctl1",		{Sctlqid},	0,			0600,
463e12c5d1SDavid du Colombier };
473e12c5d1SDavid du Colombier #define NPIPEDIR 4
483e12c5d1SDavid du Colombier 
493e12c5d1SDavid du Colombier void
503e12c5d1SDavid du Colombier pipeinit(void)
513e12c5d1SDavid du Colombier {
523e12c5d1SDavid du Colombier }
533e12c5d1SDavid du Colombier 
543e12c5d1SDavid du Colombier void
553e12c5d1SDavid du Colombier pipereset(void)
563e12c5d1SDavid du Colombier {
573e12c5d1SDavid du Colombier }
583e12c5d1SDavid du Colombier 
593e12c5d1SDavid du Colombier /*
603e12c5d1SDavid du Colombier  *  create a pipe, no streams are created until an open
613e12c5d1SDavid du Colombier  */
623e12c5d1SDavid du Colombier Chan*
633e12c5d1SDavid du Colombier pipeattach(char *spec)
643e12c5d1SDavid du Colombier {
653e12c5d1SDavid du Colombier 	Pipe *p;
663e12c5d1SDavid du Colombier 	Chan *c;
673e12c5d1SDavid du Colombier 
683e12c5d1SDavid du Colombier 	c = devattach('|', spec);
693e12c5d1SDavid du Colombier 	p = smalloc(sizeof(Pipe));
703e12c5d1SDavid du Colombier 	p->ref = 1;
713e12c5d1SDavid du Colombier 
723e12c5d1SDavid du Colombier 	lock(&pipealloc);
733e12c5d1SDavid du Colombier 	p->path = ++pipealloc.path;
743e12c5d1SDavid du Colombier 	p->next = pipealloc.pipe;
753e12c5d1SDavid du Colombier 	pipealloc.pipe = p;
763e12c5d1SDavid du Colombier 	unlock(&pipealloc);
773e12c5d1SDavid du Colombier 
783e12c5d1SDavid du Colombier 	c->qid = (Qid){CHDIR|STREAMQID(2*p->path, 0), 0};
793e12c5d1SDavid du Colombier 	c->dev = 0;
803e12c5d1SDavid du Colombier 	return c;
813e12c5d1SDavid du Colombier }
823e12c5d1SDavid du Colombier 
833e12c5d1SDavid du Colombier Chan*
843e12c5d1SDavid du Colombier pipeclone(Chan *c, Chan *nc)
853e12c5d1SDavid du Colombier {
863e12c5d1SDavid du Colombier 	Pipe *p;
873e12c5d1SDavid du Colombier 
883e12c5d1SDavid du Colombier 	p = getpipe(STREAMID(c->qid.path)/2);
893e12c5d1SDavid du Colombier 	nc = devclone(c, nc);
903e12c5d1SDavid du Colombier 	if(incref(p) <= 1)
913e12c5d1SDavid du Colombier 		panic("pipeclone");
923e12c5d1SDavid du Colombier 	return nc;
933e12c5d1SDavid du Colombier }
943e12c5d1SDavid du Colombier 
953e12c5d1SDavid du Colombier int
963e12c5d1SDavid du Colombier pipegen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
973e12c5d1SDavid du Colombier {
983e12c5d1SDavid du Colombier 	int id;
993e12c5d1SDavid du Colombier 
1003e12c5d1SDavid du Colombier 	id = STREAMID(c->qid.path);
1013e12c5d1SDavid du Colombier 	if(i > 1)
1023e12c5d1SDavid du Colombier 		id++;
1033e12c5d1SDavid du Colombier 	if(tab==0 || i>=ntab)
1043e12c5d1SDavid du Colombier 		return -1;
1053e12c5d1SDavid du Colombier 	tab += i;
1063e12c5d1SDavid du Colombier 	devdir(c, (Qid){STREAMQID(id, tab->qid.path),0}, tab->name, tab->length, eve, tab->perm, dp);
1073e12c5d1SDavid du Colombier 	return 1;
1083e12c5d1SDavid du Colombier }
1093e12c5d1SDavid du Colombier 
1103e12c5d1SDavid du Colombier 
1113e12c5d1SDavid du Colombier int
1123e12c5d1SDavid du Colombier pipewalk(Chan *c, char *name)
1133e12c5d1SDavid du Colombier {
1143e12c5d1SDavid du Colombier 	return devwalk(c, name, pipedir, NPIPEDIR, pipegen);
1153e12c5d1SDavid du Colombier }
1163e12c5d1SDavid du Colombier 
1173e12c5d1SDavid du Colombier void
1183e12c5d1SDavid du Colombier pipestat(Chan *c, char *db)
1193e12c5d1SDavid du Colombier {
1203e12c5d1SDavid du Colombier 	streamstat(c, db, "pipe", 0666);
1213e12c5d1SDavid du Colombier }
1223e12c5d1SDavid du Colombier 
1233e12c5d1SDavid du Colombier /*
1243e12c5d1SDavid du Colombier  *  if the stream doesn't exist, create it
1253e12c5d1SDavid du Colombier  */
1263e12c5d1SDavid du Colombier Chan *
1273e12c5d1SDavid du Colombier pipeopen(Chan *c, int omode)
1283e12c5d1SDavid du Colombier {
1293e12c5d1SDavid du Colombier 	Pipe *p;
1303e12c5d1SDavid du Colombier 	int other;
1313e12c5d1SDavid du Colombier 	Stream *local, *remote;
1323e12c5d1SDavid du Colombier 
1333e12c5d1SDavid du Colombier 	if(c->qid.path & CHDIR){
1343e12c5d1SDavid du Colombier 		if(omode != OREAD)
1353e12c5d1SDavid du Colombier 			error(Ebadarg);
1363e12c5d1SDavid du Colombier 		c->mode = omode;
1373e12c5d1SDavid du Colombier 		c->flag |= COPEN;
1383e12c5d1SDavid du Colombier 		c->offset = 0;
1393e12c5d1SDavid du Colombier 		return c;
1403e12c5d1SDavid du Colombier 	}
1413e12c5d1SDavid du Colombier 
1423e12c5d1SDavid du Colombier 	p = getpipe(STREAMID(c->qid.path)/2);
1433e12c5d1SDavid du Colombier 	if(waserror()){
1443e12c5d1SDavid du Colombier 		qunlock(p);
1453e12c5d1SDavid du Colombier 		nexterror();
1463e12c5d1SDavid du Colombier 	}
1473e12c5d1SDavid du Colombier 	qlock(p);
1483e12c5d1SDavid du Colombier 	streamopen(c, &pipeinfo);
1493e12c5d1SDavid du Colombier 	local = c->stream;
1503e12c5d1SDavid du Colombier 	if(local->devq->ptr == 0){
1513e12c5d1SDavid du Colombier 		/*
1523e12c5d1SDavid du Colombier 		 *  first open, create the other end also
1533e12c5d1SDavid du Colombier 		 */
1543e12c5d1SDavid du Colombier 		other = STREAMID(c->qid.path)^1;
1553e12c5d1SDavid du Colombier 		remote = streamnew(c->type, c->dev, other, &pipeinfo,1);
1563e12c5d1SDavid du Colombier 
1573e12c5d1SDavid du Colombier 		/*
1583e12c5d1SDavid du Colombier 		 *  connect the device ends of both streams
1593e12c5d1SDavid du Colombier 		 */
1603e12c5d1SDavid du Colombier 		local->devq->ptr = remote;
1613e12c5d1SDavid du Colombier 		remote->devq->ptr = local;
1623e12c5d1SDavid du Colombier 		local->devq->other->next = remote->devq;
1633e12c5d1SDavid du Colombier 		remote->devq->other->next = local->devq;
1643e12c5d1SDavid du Colombier 	} else if(local->opens == 1){
1653e12c5d1SDavid du Colombier 		/*
1663e12c5d1SDavid du Colombier 		 *  keep other side around till last close of this side
1673e12c5d1SDavid du Colombier 		 */
1683e12c5d1SDavid du Colombier 		streamenter(local->devq->ptr);
1693e12c5d1SDavid du Colombier 	}
1703e12c5d1SDavid du Colombier 	qunlock(p);
1713e12c5d1SDavid du Colombier 	poperror();
1723e12c5d1SDavid du Colombier 
173*219b2ee8SDavid du Colombier 	c->mode = openmode(omode);
1743e12c5d1SDavid du Colombier 	c->flag |= COPEN;
1753e12c5d1SDavid du Colombier 	c->offset = 0;
1763e12c5d1SDavid du Colombier 	return c;
1773e12c5d1SDavid du Colombier }
1783e12c5d1SDavid du Colombier 
1793e12c5d1SDavid du Colombier void
1803e12c5d1SDavid du Colombier pipecreate(Chan *c, char *name, int omode, ulong perm)
1813e12c5d1SDavid du Colombier {
1823e12c5d1SDavid du Colombier 	USED(c, name, omode, perm);
1833e12c5d1SDavid du Colombier 	error(Egreg);
1843e12c5d1SDavid du Colombier }
1853e12c5d1SDavid du Colombier 
1863e12c5d1SDavid du Colombier void
1873e12c5d1SDavid du Colombier piperemove(Chan *c)
1883e12c5d1SDavid du Colombier {
1893e12c5d1SDavid du Colombier 	USED(c);
1903e12c5d1SDavid du Colombier 	error(Egreg);
1913e12c5d1SDavid du Colombier }
1923e12c5d1SDavid du Colombier 
1933e12c5d1SDavid du Colombier void
1943e12c5d1SDavid du Colombier pipewstat(Chan *c, char *db)
1953e12c5d1SDavid du Colombier {
1963e12c5d1SDavid du Colombier 	USED(c, db);
1973e12c5d1SDavid du Colombier 	error(Eperm);
1983e12c5d1SDavid du Colombier }
1993e12c5d1SDavid du Colombier 
2003e12c5d1SDavid du Colombier void
2013e12c5d1SDavid du Colombier pipeclose(Chan *c)
2023e12c5d1SDavid du Colombier {
2033e12c5d1SDavid du Colombier 	Pipe *p, *f, **l;
2043e12c5d1SDavid du Colombier 	Stream *remote;
2053e12c5d1SDavid du Colombier 
2063e12c5d1SDavid du Colombier 	p = getpipe(STREAMID(c->qid.path)/2);
2073e12c5d1SDavid du Colombier 
2083e12c5d1SDavid du Colombier 	/*
2093e12c5d1SDavid du Colombier 	 *  take care of local and remote streams
2103e12c5d1SDavid du Colombier 	 */
2113e12c5d1SDavid du Colombier 	if(c->stream){
2123e12c5d1SDavid du Colombier 		qlock(p);
2133e12c5d1SDavid du Colombier 		remote = c->stream->devq->ptr;
2143e12c5d1SDavid du Colombier 		if(streamclose(c) == 0){
2153e12c5d1SDavid du Colombier 			if(remote)
2163e12c5d1SDavid du Colombier 				streamexit(remote);
2173e12c5d1SDavid du Colombier 		}
2183e12c5d1SDavid du Colombier 		qunlock(p);
2193e12c5d1SDavid du Colombier 	}
2203e12c5d1SDavid du Colombier 
2213e12c5d1SDavid du Colombier 	/*
2223e12c5d1SDavid du Colombier 	 *  free the structure
2233e12c5d1SDavid du Colombier 	 */
2243e12c5d1SDavid du Colombier 	if(decref(p) == 0){
2253e12c5d1SDavid du Colombier 		lock(&pipealloc);
2263e12c5d1SDavid du Colombier 		l = &pipealloc.pipe;
2273e12c5d1SDavid du Colombier 		for(f = *l; f; f = f->next) {
2283e12c5d1SDavid du Colombier 			if(f == p) {
2293e12c5d1SDavid du Colombier 				*l = p->next;
2303e12c5d1SDavid du Colombier 				break;
2313e12c5d1SDavid du Colombier 			}
2323e12c5d1SDavid du Colombier 			l = &f->next;
2333e12c5d1SDavid du Colombier 		}
2343e12c5d1SDavid du Colombier 		unlock(&pipealloc);
2353e12c5d1SDavid du Colombier 		free(p);
2363e12c5d1SDavid du Colombier 	}
2373e12c5d1SDavid du Colombier }
2383e12c5d1SDavid du Colombier 
2393e12c5d1SDavid du Colombier long
2403e12c5d1SDavid du Colombier piperead(Chan *c, void *va, long n, ulong offset)
2413e12c5d1SDavid du Colombier {
2423e12c5d1SDavid du Colombier 	USED(offset);
2433e12c5d1SDavid du Colombier 	if(c->qid.path & CHDIR)
2443e12c5d1SDavid du Colombier 		return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen);
2453e12c5d1SDavid du Colombier 
2463e12c5d1SDavid du Colombier 	return streamread(c, va, n);
2473e12c5d1SDavid du Colombier }
2483e12c5d1SDavid du Colombier 
2493e12c5d1SDavid du Colombier /*
2503e12c5d1SDavid du Colombier  *  a write to a closed pipe causes a note to be sent to
2513e12c5d1SDavid du Colombier  *  the process.
2523e12c5d1SDavid du Colombier  */
2533e12c5d1SDavid du Colombier long
2543e12c5d1SDavid du Colombier pipewrite(Chan *c, void *va, long n, ulong offset)
2553e12c5d1SDavid du Colombier {
2563e12c5d1SDavid du Colombier 	USED(offset);
2573e12c5d1SDavid du Colombier 
2583e12c5d1SDavid du Colombier 	/* avoid notes when pipe is a mounted stream */
2593e12c5d1SDavid du Colombier 	if(c->flag & CMSG)
2603e12c5d1SDavid du Colombier 		return streamwrite(c, va, n, 0);
2613e12c5d1SDavid du Colombier 
2623e12c5d1SDavid du Colombier 	if(waserror()) {
2633e12c5d1SDavid du Colombier 		postnote(u->p, 1, "sys: write on closed pipe", NUser);
264*219b2ee8SDavid du Colombier 		error(Ehungup);
2653e12c5d1SDavid du Colombier 	}
2663e12c5d1SDavid du Colombier 	n = streamwrite(c, va, n, 0);
2673e12c5d1SDavid du Colombier 	poperror();
2683e12c5d1SDavid du Colombier 	return n;
2693e12c5d1SDavid du Colombier }
2703e12c5d1SDavid du Colombier 
2713e12c5d1SDavid du Colombier /*
2723e12c5d1SDavid du Colombier  *  send a block upstream to the process.
2733e12c5d1SDavid du Colombier  *  sleep until there's room upstream.
2743e12c5d1SDavid du Colombier  */
2753e12c5d1SDavid du Colombier static void
2763e12c5d1SDavid du Colombier pipeiput(Queue *q, Block *bp)
2773e12c5d1SDavid du Colombier {
2783e12c5d1SDavid du Colombier 	FLOWCTL(q, bp);
2793e12c5d1SDavid du Colombier }
2803e12c5d1SDavid du Colombier 
2813e12c5d1SDavid du Colombier /*
2823e12c5d1SDavid du Colombier  *  send the block to the other side
2833e12c5d1SDavid du Colombier  */
2843e12c5d1SDavid du Colombier static void
2853e12c5d1SDavid du Colombier pipeoput(Queue *q, Block *bp)
2863e12c5d1SDavid du Colombier {
2873e12c5d1SDavid du Colombier 	PUTNEXT(q, bp);
2883e12c5d1SDavid du Colombier }
2893e12c5d1SDavid du Colombier 
2903e12c5d1SDavid du Colombier /*
2913e12c5d1SDavid du Colombier  *  send a hangup and disconnect the streams
2923e12c5d1SDavid du Colombier  */
2933e12c5d1SDavid du Colombier static void
2943e12c5d1SDavid du Colombier pipestclose(Queue *q)
2953e12c5d1SDavid du Colombier {
2963e12c5d1SDavid du Colombier 	Block *bp;
2973e12c5d1SDavid du Colombier 
2983e12c5d1SDavid du Colombier 	/*
2993e12c5d1SDavid du Colombier 	 *  point to the bit-bucket and let any in-progress
3003e12c5d1SDavid du Colombier 	 *  write's finish.
3013e12c5d1SDavid du Colombier 	 */
3023e12c5d1SDavid du Colombier 	q->put = nullput;
3033e12c5d1SDavid du Colombier 	wakeup(&q->r);
3043e12c5d1SDavid du Colombier 
3053e12c5d1SDavid du Colombier 	/*
3063e12c5d1SDavid du Colombier 	 *  send a hangup
3073e12c5d1SDavid du Colombier 	 */
3083e12c5d1SDavid du Colombier 	q = q->other;
3093e12c5d1SDavid du Colombier 	if(q->next == 0)
3103e12c5d1SDavid du Colombier 		return;
3113e12c5d1SDavid du Colombier 	bp = allocb(0);
3123e12c5d1SDavid du Colombier 	bp->type = M_HANGUP;
3133e12c5d1SDavid du Colombier 	PUTNEXT(q, bp);
3143e12c5d1SDavid du Colombier }
3153e12c5d1SDavid du Colombier 
3163e12c5d1SDavid du Colombier Pipe*
3173e12c5d1SDavid du Colombier getpipe(ulong path)
3183e12c5d1SDavid du Colombier {
3193e12c5d1SDavid du Colombier 	Pipe *p;
3203e12c5d1SDavid du Colombier 
3213e12c5d1SDavid du Colombier 	lock(&pipealloc);
3223e12c5d1SDavid du Colombier 	for(p = pipealloc.pipe; p; p = p->next) {
3233e12c5d1SDavid du Colombier 		if(path == p->path) {
3243e12c5d1SDavid du Colombier 			unlock(&pipealloc);
3253e12c5d1SDavid du Colombier 			return p;
3263e12c5d1SDavid du Colombier 		}
3273e12c5d1SDavid du Colombier 	}
3283e12c5d1SDavid du Colombier 	unlock(&pipealloc);
3293e12c5d1SDavid du Colombier 	panic("getpipe");
3303e12c5d1SDavid du Colombier 	return 0;		/* not reached */
3313e12c5d1SDavid du Colombier }
332