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