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