xref: /inferno-os/emu/port/devpipe.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 #include	<interp.h>
5 
6 #define NETTYPE(x)	((ulong)(x)&0x1f)
7 #define NETID(x)	(((ulong)(x))>>5)
8 #define NETQID(i,t)	(((i)<<5)|(t))
9 
10 typedef struct Pipe	Pipe;
11 struct Pipe
12 {
13 	QLock	l;
14 	Pipe*	next;
15 	int	ref;
16 	ulong	path;
17 	Queue*	q[2];
18 	int	qref[2];
19 	Dirtab*	pipedir;
20 	char*	user;
21 };
22 
23 static struct
24 {
25 	Lock	l;
26 	ulong	path;
27 	int	pipeqsize;
28 } pipealloc;
29 
30 enum
31 {
32 	Qdir,
33 	Qdata0,
34 	Qdata1
35 };
36 
37 Dirtab pipedir[] =
38 {
39 	".",		{Qdir,0,QTDIR},	0,		DMDIR|0500,
40 	"data",		{Qdata0},	0,			0660,
41 	"data1",	{Qdata1},	0,			0660,
42 };
43 
44 static void
45 freepipe(Pipe *p)
46 {
47 	if(p != nil){
48 		free(p->user);
49 		free(p->q[0]);
50 		free(p->q[1]);
51 		free(p->pipedir);
52 		free(p);
53 	}
54 }
55 
56 static void
57 pipeinit(void)
58 {
59 	pipealloc.pipeqsize = 32*1024;
60 }
61 
62 /*
63  *  create a pipe, no streams are created until an open
64  */
65 static Chan*
66 pipeattach(char *spec)
67 {
68 	Pipe *p;
69 	Chan *c;
70 
71 	c = devattach('|', spec);
72 	p = malloc(sizeof(Pipe));
73 	if(p == 0)
74 		error(Enomem);
75 	if(waserror()){
76 		freepipe(p);
77 		nexterror();
78 	}
79 	p->pipedir = malloc(sizeof(pipedir));
80 	if (p->pipedir == 0)
81 		error(Enomem);
82 	memmove(p->pipedir, pipedir, sizeof(pipedir));
83 	kstrdup(&p->user, up->env->user);
84 	p->ref = 1;
85 
86 	p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0);
87 	if(p->q[0] == 0)
88 		error(Enomem);
89 	p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0);
90 	if(p->q[1] == 0)
91 		error(Enomem);
92 	poperror();
93 
94 	lock(&pipealloc.l);
95 	p->path = ++pipealloc.path;
96 	unlock(&pipealloc.l);
97 
98 	c->qid.path = NETQID(2*p->path, Qdir);
99 	c->qid.vers = 0;
100 	c->qid.type = QTDIR;
101 	c->aux = p;
102 	c->dev = 0;
103 	return c;
104 }
105 
106 static int
107 pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
108 {
109 	int id, len;
110 	Qid qid;
111 	Pipe *p;
112 
113 	USED(name);
114 	if(i == DEVDOTDOT){
115 		devdir(c, c->qid, "#|", 0, eve, 0555, dp);
116 		return 1;
117 	}
118 	i++;	/* skip . */
119 	if(tab==0 || i>=ntab)
120 		return -1;
121 	tab += i;
122 	p = c->aux;
123 	switch(NETTYPE(tab->qid.path)){
124 	case Qdata0:
125 		len = qlen(p->q[0]);
126 		break;
127 	case Qdata1:
128 		len = qlen(p->q[1]);
129 		break;
130 	default:
131 		len = tab->length;
132 		break;
133 	}
134 	id = NETID(c->qid.path);
135 	qid.path = NETQID(id, tab->qid.path);
136 	qid.vers = 0;
137 	qid.type = QTFILE;
138 	devdir(c, qid, tab->name, len, eve, tab->perm, dp);
139 	return 1;
140 }
141 
142 static Walkqid*
143 pipewalk(Chan *c, Chan *nc, char **name, int nname)
144 {
145 	Walkqid *wq;
146 	Pipe *p;
147 
148 	p = c->aux;
149 	wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen);
150 	if(wq != nil && wq->clone != nil && wq->clone != c){
151 		qlock(&p->l);
152 		p->ref++;
153 		if(c->flag & COPEN){
154 			switch(NETTYPE(c->qid.path)){
155 			case Qdata0:
156 				p->qref[0]++;
157 				break;
158 			case Qdata1:
159 				p->qref[1]++;
160 				break;
161 			}
162 		}
163 		qunlock(&p->l);
164 	}
165 	return wq;
166 }
167 
168 static int
169 pipestat(Chan *c, uchar *db, int n)
170 {
171 	Pipe *p;
172 	Dir dir;
173 	Dirtab *tab;
174 
175 	p = c->aux;
176 	tab = p->pipedir;
177 
178 	switch(NETTYPE(c->qid.path)){
179 	case Qdir:
180 		devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
181 		break;
182 	case Qdata0:
183 		devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir);
184 		break;
185 	case Qdata1:
186 		devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir);
187 		break;
188 	default:
189 		panic("pipestat");
190 	}
191 	n = convD2M(&dir, db, n);
192 	if(n < BIT16SZ)
193 		error(Eshortstat);
194 	return n;
195 }
196 
197 /*
198  *  if the stream doesn't exist, create it
199  */
200 static Chan*
201 pipeopen(Chan *c, int omode)
202 {
203 	Pipe *p;
204 
205 	if(c->qid.type & QTDIR){
206 		if(omode != OREAD)
207 			error(Ebadarg);
208 		c->mode = omode;
209 		c->flag |= COPEN;
210 		c->offset = 0;
211 		return c;
212 	}
213 
214 	openmode(omode);	/* check it */
215 
216 	p = c->aux;
217 	qlock(&p->l);
218 	if(waserror()){
219 		qunlock(&p->l);
220 		nexterror();
221 	}
222 	switch(NETTYPE(c->qid.path)){
223 	case Qdata0:
224 		devpermcheck(p->user, p->pipedir[1].perm, omode);
225 		p->qref[0]++;
226 		break;
227 	case Qdata1:
228 		devpermcheck(p->user, p->pipedir[2].perm, omode);
229 		p->qref[1]++;
230 		break;
231 	}
232 	poperror();
233 	qunlock(&p->l);
234 
235 	c->mode = openmode(omode);
236 	c->flag |= COPEN;
237 	c->offset = 0;
238 	c->iounit = qiomaxatomic;
239 	return c;
240 }
241 
242 static void
243 pipeclose(Chan *c)
244 {
245 	Pipe *p;
246 
247 	p = c->aux;
248 	qlock(&p->l);
249 
250 	if(c->flag & COPEN){
251 		/*
252 		 *  closing either side hangs up the stream
253 		 */
254 		switch(NETTYPE(c->qid.path)){
255 		case Qdata0:
256 			p->qref[0]--;
257 			if(p->qref[0] == 0){
258 				qhangup(p->q[1], 0);
259 				qclose(p->q[0]);
260 			}
261 			break;
262 		case Qdata1:
263 			p->qref[1]--;
264 			if(p->qref[1] == 0){
265 				qhangup(p->q[0], 0);
266 				qclose(p->q[1]);
267 			}
268 			break;
269 		}
270 	}
271 
272 
273 	/*
274 	 *  if both sides are closed, they are reusable
275 	 */
276 	if(p->qref[0] == 0 && p->qref[1] == 0){
277 		qreopen(p->q[0]);
278 		qreopen(p->q[1]);
279 	}
280 
281 	/*
282 	 *  free the structure on last close
283 	 */
284 	p->ref--;
285 	if(p->ref == 0){
286 		qunlock(&p->l);
287 		freepipe(p);
288 	} else
289 		qunlock(&p->l);
290 }
291 
292 static long
293 piperead(Chan *c, void *va, long n, vlong junk)
294 {
295 	Pipe *p;
296 
297 	p = c->aux;
298 
299 	USED(junk);
300 	switch(NETTYPE(c->qid.path)){
301 	case Qdir:
302 		return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen);
303 	case Qdata0:
304 		return qread(p->q[0], va, n);
305 	case Qdata1:
306 		return qread(p->q[1], va, n);
307 	default:
308 		panic("piperead");
309 	}
310 	return -1;	/* not reached */
311 }
312 
313 static Block*
314 pipebread(Chan *c, long n, ulong offset)
315 {
316 	Pipe *p;
317 
318 	p = c->aux;
319 
320 	switch(NETTYPE(c->qid.path)){
321 	case Qdata0:
322 		return qbread(p->q[0], n);
323 	case Qdata1:
324 		return qbread(p->q[1], n);
325 	}
326 
327 	return devbread(c, n, offset);
328 }
329 
330 /*
331  *  a write to a closed pipe causes an exception to be sent to
332  *  the prog.
333  */
334 static long
335 pipewrite(Chan *c, void *va, long n, vlong junk)
336 {
337 	Pipe *p;
338 	Prog *r;
339 
340 	USED(junk);
341 	if(waserror()) {
342 		/* avoid exceptions when pipe is a mounted queue */
343 		if((c->flag & CMSG) == 0) {
344 			r = up->iprog;
345 			if(r != nil && r->kill == nil)
346 				r->kill = "write on closed pipe";
347 		}
348 		nexterror();
349 	}
350 
351 	p = c->aux;
352 
353 	switch(NETTYPE(c->qid.path)){
354 	case Qdata0:
355 		n = qwrite(p->q[1], va, n);
356 		break;
357 
358 	case Qdata1:
359 		n = qwrite(p->q[0], va, n);
360 		break;
361 
362 	default:
363 		panic("pipewrite");
364 	}
365 
366 	poperror();
367 	return n;
368 }
369 
370 static long
371 pipebwrite(Chan *c, Block *bp, ulong junk)
372 {
373 	long n;
374 	Pipe *p;
375 	Prog *r;
376 
377 	USED(junk);
378 	if(waserror()) {
379 		/* avoid exceptions when pipe is a mounted queue */
380 		if((c->flag & CMSG) == 0) {
381 			r = up->iprog;
382 			if(r != nil && r->kill == nil)
383 				r->kill = "write on closed pipe";
384 		}
385 		nexterror();
386 	}
387 
388 	p = c->aux;
389 	switch(NETTYPE(c->qid.path)){
390 	case Qdata0:
391 		n = qbwrite(p->q[1], bp);
392 		break;
393 
394 	case Qdata1:
395 		n = qbwrite(p->q[0], bp);
396 		break;
397 
398 	default:
399 		n = 0;
400 		panic("pipebwrite");
401 	}
402 
403 	poperror();
404 	return n;
405 }
406 
407 static int
408 pipewstat(Chan *c, uchar *dp, int n)
409 {
410 	Dir *d;
411 	Pipe *p;
412 	int d1;
413 
414 	if (c->qid.type&QTDIR)
415 		error(Eperm);
416 	p = c->aux;
417 	if(strcmp(up->env->user, p->user) != 0)
418 		error(Eperm);
419 	d = smalloc(sizeof(*d)+n);
420 	if(waserror()){
421 		free(d);
422 		nexterror();
423 	}
424 	n = convM2D(dp, n, d, (char*)&d[1]);
425 	if(n == 0)
426 		error(Eshortstat);
427 	d1 = NETTYPE(c->qid.path) == Qdata1;
428 	if(!emptystr(d->name)){
429 		validwstatname(d->name);
430 		if(strlen(d->name) >= KNAMELEN)
431 			error(Efilename);
432 		if(strcmp(p->pipedir[1+!d1].name, d->name) == 0)
433 			error(Eexist);
434 		kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN);
435 	}
436 	if(d->mode != ~0UL)
437 		p->pipedir[d1 + 1].perm = d->mode & 0777;
438 	poperror();
439 	free(d);
440 	return n;
441 }
442 
443 Dev pipedevtab = {
444 	'|',
445 	"pipe",
446 
447 	pipeinit,
448 	pipeattach,
449 	pipewalk,
450 	pipestat,
451 	pipeopen,
452 	devcreate,
453 	pipeclose,
454 	piperead,
455 	pipebread,
456 	pipewrite,
457 	pipebwrite,
458 	devremove,
459 	pipewstat,
460 };
461