]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/consolefs.c
0d4146bc6c714ef42c750d6604a16764c92fe504
[plan9front.git] / sys / src / cmd / aux / consolefs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <bio.h>
6 #include <ndb.h>
7 #include <thread.h>
8 #include <9p.h>
9
10 /*
11  *  This fs presents a 1 level file system.  It contains
12  *  up to three files per console (xxx and xxxctl and xxxstat)
13  */
14
15 typedef struct Console Console;
16 typedef struct Aux Aux;
17 typedef struct Request Request;
18 typedef struct Reqlist Reqlist;
19 typedef struct Fs Fs;
20
21 enum
22 {
23         /* last 5 bits of qid.path */
24         Textern=        0,              /* fake parent of top level */
25         Ttopdir,                        /* top level directory */
26         Qctl,
27         Qstat,
28         Qdata,
29
30         Bufsize=        32*1024,        /* chars buffered per reader */
31         Maxcons=        64,             /* maximum consoles */
32         Nhash=          64,             /* Aux hash buckets */
33 };
34
35 #define TYPE(x)         (((ulong)x.path) & 0xf)
36 #define CONS(x)         ((((ulong)x.path) >> 4)&0xfff)
37 #define QID(c, x)       (((c)<<4) | (x))
38
39 struct Request
40 {
41         Request *next;
42         Aux     *fid;
43         Fs      *fs;
44         Fcall   f;
45         uchar   buf[1];
46 };
47
48 struct Reqlist
49 {
50         Lock;
51         Request *first;
52         Request *last;
53 };
54
55 struct Aux
56 {
57         Lock;
58         Aux     *next;                  /* hash list */
59         Aux     *cnext;                 /* list of Aux's on a console */
60         int     fid;
61         int     ref;
62
63         int     attached;
64         int     open;
65         char    *user;
66         char    mbuf[Bufsize];          /* message */
67         int     bufn;
68         int     used;
69         Qid     qid;
70
71         Console *c;
72
73         char    buf[Bufsize];
74         char    *rp;
75         char    *wp;
76
77         Reqlist r;                      /* active read requests */
78 };
79
80 struct Console
81 {
82         Lock;
83
84         char    *name;
85         char    *dev;
86         int     speed;
87         int     cronly;
88         int     ondemand;               /* open only on demand */
89         int     chat;                   /* chat consoles are special */
90
91         int     pid;                    /* pid of reader */
92
93         int     fd;
94         int     cfd;
95         int     sfd;
96
97         Aux     *flist;                 /* open fids to broadcast to */
98 };
99
100 struct Fs
101 {
102         Lock;
103
104         int     fd;                     /* to kernel mount point */
105         int     messagesize;
106         Aux     *hash[Nhash];
107         Console *cons[Maxcons];
108         int     ncons;
109 };
110
111 extern  void    console(Fs*, char*, char*, int, int, int);
112 extern  Fs*     fsmount(char*);
113
114 extern  void    fsreader(void*);
115 extern  void    fsrun(void*);
116 extern  Aux*    fsgetfid(Fs*, int);
117 extern  void    fsputfid(Fs*, Aux*);
118 extern  int     fsdirgen(Fs*, Qid, int, Dir*, uchar*, int);
119 extern  void    fsreply(Fs*, Request*, char*);
120 extern  void    fskick(Fs*, Aux*);
121 extern  int     fsreopen(Fs*, Console*);
122
123 extern  void    fsversion(Fs*, Request*, Aux*);
124 extern  void    fsflush(Fs*, Request*, Aux*);
125 extern  void    fsauth(Fs*, Request*, Aux*);
126 extern  void    fsattach(Fs*, Request*, Aux*);
127 extern  void    fswalk(Fs*, Request*, Aux*);
128 extern  void    fsclwalk(Fs*, Request*, Aux*);
129 extern  void    fsopen(Fs*, Request*, Aux*);
130 extern  void    fscreate(Fs*, Request*, Aux*);
131 extern  void    fsread(Fs*, Request*, Aux*);
132 extern  void    fswrite(Fs*, Request*, Aux*);
133 extern  void    fsclunk(Fs*, Request*, Aux*);
134 extern  void    fsremove(Fs*, Request*, Aux*);
135 extern  void    fsstat(Fs*, Request*, Aux*);
136 extern  void    fswstat(Fs*, Request*, Aux*);
137
138
139 void    (*fcall[])(Fs*, Request*, Aux*) =
140 {
141         [Tflush]        fsflush,
142         [Tversion]      fsversion,
143         [Tauth] fsauth,
144         [Tattach]       fsattach,
145         [Twalk]         fswalk,
146         [Topen]         fsopen,
147         [Tcreate]       fscreate,
148         [Tread]         fsread,
149         [Twrite]        fswrite,
150         [Tclunk]        fsclunk,
151         [Tremove]       fsremove,
152         [Tstat]         fsstat,
153         [Twstat]        fswstat
154 };
155
156 char Eperm[] = "permission denied";
157 char Eexist[] = "file does not exist";
158 char Enotdir[] = "not a directory";
159 char Eisopen[] = "file already open";
160 char Ebadcount[] = "bad read/write count";
161 char Enofid[] = "no such fid";
162
163 char *consoledb = "/lib/ndb/consoledb";
164 char *mntpt = "/mnt/consoles";
165
166 int messagesize = 8192+IOHDRSZ;
167
168 void
169 fatal(char *fmt, ...)
170 {
171         va_list arg;
172         char buf[1024];
173
174         write(2, "consolefs: ", 10);
175         va_start(arg, fmt);
176         vseprint(buf, buf+1024, fmt, arg);
177         va_end(arg);
178         write(2, buf, strlen(buf));
179         write(2, "\n", 1);
180         threadexitsall(fmt);
181 }
182
183
184 void*
185 emalloc(uint n)
186 {
187         void *p;
188
189         p = malloc(n);
190         if(p == nil)
191                 fatal("malloc failed: %r");
192         memset(p, 0, n);
193         return p;
194 }
195
196 int debug;
197 Ndb *db;
198
199 /*
200  *  any request that can get queued for a delayed reply
201  */
202 Request*
203 alloccreq(Fs *fs, int bufsize)
204 {
205         Request *r;
206
207         r = emalloc(sizeof(Request)+bufsize);
208         r->fs = fs;
209         r->next = nil;
210         return r;
211 }
212
213 /*
214  *  for maintaining lists of requests
215  */
216 void
217 addreq(Reqlist *l, Request *r)
218 {
219         lock(l);
220         if(l->first == nil)
221                 l->first = r;
222         else
223                 l->last->next = r;
224         l->last = r;
225         r->next = nil;
226         unlock(l);
227 }
228
229 /*
230  *  remove the first request from a list of requests
231  */
232 Request*
233 remreq(Reqlist *l)
234 {
235         Request *r;
236
237         lock(l);
238         r = l->first;
239         if(r != nil)
240                 l->first = r->next;
241         unlock(l);
242         return r;
243 }
244
245 /*
246  *  remove a request with the given tag from a list of requests
247  */
248 Request*
249 remtag(Reqlist *l, int tag)
250 {
251         Request *or, **ll;
252
253         lock(l);
254         ll = &l->first;
255         for(or = *ll; or; or = or->next){
256                 if(or->f.tag == tag){
257                         *ll = or->next;
258                         unlock(l);
259                         return or;
260                 }
261                 ll = &or->next;
262         }
263         unlock(l);
264         return nil;
265 }
266
267 Qid
268 parentqid(Qid q)
269 {
270         if(q.type & QTDIR)
271                 return (Qid){QID(0, Textern), 0, QTDIR};
272         else
273                 return (Qid){QID(0, Ttopdir), 0, QTDIR};
274 }
275
276 int
277 fsdirgen(Fs *fs, Qid parent, int i, Dir *d, uchar *buf, int nbuf)
278 {
279         static char name[64];
280         char *p;
281         int xcons;
282
283         d->uid = d->gid = d->muid = "network";
284         d->length = 0;
285         d->atime = time(nil);
286         d->mtime = d->atime;
287         d->type = 'C';
288         d->dev = '0';
289
290         switch(TYPE(parent)){
291         case Textern:
292                 if(i != 0)
293                         return -1;
294                 p = "consoles";
295                 d->mode = DMDIR|0555;
296                 d->qid.type = QTDIR;
297                 d->qid.path = QID(0, Ttopdir);
298                 d->qid.vers = 0;
299                 break;
300         case Ttopdir:
301                 xcons = i/3;
302                 if(xcons >= fs->ncons)
303                         return -1;
304                 p = fs->cons[xcons]->name;
305                 switch(i%3){
306                 case 0:
307                         if(fs->cons[xcons]->cfd < 0)
308                                 return 0;
309                         snprint(name, sizeof name, "%sctl", p);
310                         p = name;
311                         d->qid.type = QTFILE;
312                         d->qid.path = QID(xcons, Qctl);
313                         d->qid.vers = 0;
314                         break;
315                 case 1:
316                         if(fs->cons[xcons]->sfd < 0)
317                                 return 0;
318                         snprint(name, sizeof name, "%sstat", p);
319                         p = name;
320                         d->qid.type = QTFILE;
321                         d->qid.path = QID(xcons, Qstat);
322                         d->qid.vers = 0;
323                         break;
324                 case 2:
325                         d->qid.type = QTFILE;
326                         d->qid.path = QID(xcons, Qdata);
327                         d->qid.vers = 0;
328                         break;
329                 }
330                 d->mode = 0666;
331                 break;
332         default:
333                 return -1;
334         }
335         d->name = p;
336         if(buf != nil)
337                 return convD2M(d, buf, nbuf);
338         return 1;
339 }
340
341 /*
342  *  mount the user interface and start a request processor
343  */
344 Fs*
345 fsmount(char *mntpt)
346 {
347         int pfd[2];
348
349         Fs *fs;
350         Dir d;
351         static void *v[2];
352
353         fs = emalloc(sizeof(Fs));
354
355         if(pipe(pfd) < 0)
356                 fatal("opening pipe: %r");
357
358         /* start up the file system process */
359         v[0] = fs;
360         v[1] = pfd;
361         proccreate(fsrun, v, 16*1024);
362
363         if(postfd("consoles", pfd[1]) < 0)
364                 sysfatal("post: %r");
365
366         nulldir(&d);
367         d.mode = 0666;
368         dirwstat("/srv/consoles", &d);
369
370         if(mntpt){
371                 if(amount(pfd[1], mntpt, MBEFORE, "") == -1)
372                         sysfatal("mount %s: %r", mntpt);
373         }else
374                 close(pfd[1]);
375
376         return fs;
377 }
378
379 /*
380  *  reopen a console
381  */
382 int
383 fsreopen(Fs* fs, Console *c)
384 {
385         char buf[128];
386         static void *v[2];
387
388         if(c->pid){
389                 if(postnote(PNPROC, c->pid, "reopen") != 0)
390                         fprint(2, "postnote failed: %r\n");
391                 c->pid = 0;
392         }
393
394         if(c->fd >= 0){
395                 close(c->fd);
396                 close(c->cfd);
397                 close(c->sfd);
398                 c->cfd = -1;
399                 c->fd = -1;
400                 c->sfd = -1;
401         }
402
403         if(c->flist == nil && c->ondemand)
404                 return 0;
405
406         c->fd = open(c->dev, ORDWR);
407         if(c->fd < 0)
408                 return -1;
409
410         snprint(buf, sizeof(buf), "%sctl", c->dev);
411         c->cfd = open(buf, ORDWR);
412         fprint(c->cfd, "b%d", c->speed);
413
414         snprint(buf, sizeof(buf), "%sstat", c->dev);
415         c->sfd = open(buf, OREAD);
416
417         v[0] = fs;
418         v[1] = c;
419         proccreate(fsreader, v, 16*1024);
420
421         return 0;
422 }
423
424 void
425 change(Fs *fs, Console *c, int doreopen, int speed, int cronly, int ondemand)
426 {
427         lock(c);
428
429         if(speed != c->speed){
430                 c->speed = speed;
431                 doreopen = 1;
432         }
433         if(ondemand != c->ondemand){
434                 c->ondemand = ondemand;
435                 doreopen = 1;
436         }
437         c->cronly = cronly;
438         if(doreopen)
439                 fsreopen(fs, c);
440
441         unlock(c);
442 }
443
444 /*
445  *  create a console interface
446  */
447 void
448 console(Fs* fs, char *name, char *dev, int speed, int cronly, int ondemand)
449 {
450         Console *c;
451         char *x;
452         int i, doreopen;
453
454         if(fs->ncons >= Maxcons)
455                 fatal("too many consoles, too little time");
456
457         doreopen = 0;
458         for(i = 0; i < fs->ncons; i++){
459                 c = fs->cons[i];
460                 if(strcmp(name, c->name) == 0){
461                         if(strcmp(dev, c->dev) != 0){
462                                 /* new device */
463                                 x = c->dev;
464                                 c->dev = strdup(dev);
465                                 free(x);
466                                 doreopen = 1;
467                         }
468                         change(fs, c, doreopen, speed, cronly, ondemand);
469                         return;
470                 }
471         }
472 #ifdef sapedoesntlikethis
473         /*
474          * The code below prevents this from working.  I can't
475          * think of scenarios where the code below actually helps
476          *      Sape
477          *
478          * console=borneo dev=/dev/eia1
479          *      speed=9600
480          *      openondemand=1
481          * console=tottie dev=/dev/eia1
482          *      speed=115200
483          *      openondemand=1
484          */
485         for(i = 0; i < fs->ncons; i++){
486                 c = fs->cons[i];
487                 if(strcmp(dev, c->dev) == 0){
488                         /* at least a rename */
489                         x = c->name;
490                         c->name = strdup(name);
491                         free(x);
492                         change(fs, c, doreopen, speed, cronly, ondemand);
493                         return;
494                 }
495         }
496 #endif
497         c = emalloc(sizeof(Console));
498         fs->cons[fs->ncons] = c;
499         fs->ncons++;
500         c->name = strdup(name);
501         c->dev = strdup(dev);
502         if(strcmp(c->dev, "/dev/null") == 0) 
503                 c->chat = 1;
504         else 
505                 c->chat = 0;
506         c->fd = -1;
507         c->cfd = -1;
508         c->sfd = -1;
509         change(fs, c, 1, speed, cronly, ondemand);
510 }
511
512 /*
513  *  buffer data from console to a client.
514  *  circular q with writer able to catch up to reader.
515  *  the reader may miss data but always sees an in order sequence.
516  */
517 void
518 fromconsole(Aux *f, char *p, int n)
519 {
520         char *rp, *wp, *ep;
521         int pass;
522
523         lock(f);
524         rp = f->rp;
525         wp = f->wp;
526         ep = f->buf + sizeof(f->buf);
527         pass = 0;
528         while(n--){
529                 *wp++ = *p++;
530                 if(wp >= ep)
531                         wp = f->buf;
532                 if(rp == wp)
533                         pass = 1;
534         }
535         f->wp = wp;
536
537         /*  we overtook the read pointer, push it up so readers always
538          *  see the tail of what was written
539          */
540         if(pass){
541                 wp++;
542                 if(wp >= ep)
543                         f->rp = f->buf;
544                 else
545                         f->rp = wp;
546         }
547         unlock(f);
548 }
549
550 /*
551  *  broadcast a list of members to all listeners
552  */
553 void
554 bcastmembers(Fs *fs, Console *c, char *msg, Aux *f)
555 {
556         int n;
557         Aux *fl;
558         char buf[512];
559
560         sprint(buf, "[%s%s", msg, f->user);
561         for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){
562                 if(f == fl)
563                         continue;
564                 strcat(buf, ", ");
565                 strcat(buf, fl->user);
566         }
567         strcat(buf, "]\n");
568
569         n = strlen(buf);
570         for(fl = c->flist; fl; fl = fl->cnext){
571                 fromconsole(fl, buf, n);
572                 fskick(fs, fl);
573         }
574 }
575
576 void
577 handler(void*, char *msg)
578 {
579         if(strstr(msg, "reopen") != nil ||
580            strstr(msg, "write on closed pipe") != nil)
581                 noted(NCONT);
582         noted(NDFLT);
583 }
584
585 /*
586  *  a process to read console output and broadcast it (one per console)
587  */
588 void
589 fsreader(void *v)
590 {
591         int n;
592         Aux *fl;
593         char buf[1024];
594         Fs *fs;
595         Console *c;
596         void **a;
597
598         a = v;
599         fs = a[0];
600         c = a[1];
601         c->pid = getpid();
602         notify(handler);
603         if(c->chat)
604                 threadexits(nil);
605         for(;;){
606                 n = read(c->fd, buf, sizeof(buf));
607                 if(n < 0)
608                         break;
609                 lock(c);
610                 for(fl = c->flist; fl; fl = fl->cnext){
611                         fromconsole(fl, buf, n);
612                         fskick(fs, fl);
613                 }
614                 unlock(c);
615         }
616 }
617
618 void
619 readdb(Fs *fs)
620 {
621         Ndbtuple *t, *nt;
622         char *dev, *cons;
623         int cronly, speed, ondemand;
624
625         ndbreopen(db);
626
627         /* start a listener for each console */
628         for(;;){
629                 t = ndbparse(db);
630                 if(t == nil)
631                         break;
632                 dev = nil;
633                 cons = nil;
634                 speed = 9600;
635                 cronly = 0;
636                 ondemand = 0;
637                 for(nt = t; nt; nt = nt->entry){
638                         if(strcmp(nt->attr, "console") == 0)
639                                 cons = nt->val;
640                         else if(strcmp(nt->attr, "dev") == 0)
641                                 dev = nt->val;
642                         else if(strcmp(nt->attr, "speed") == 0)
643                                 speed = atoi(nt->val);
644                         else if(strcmp(nt->attr, "cronly") == 0)
645                                 cronly = 1;
646                         else if(strcmp(nt->attr, "openondemand") == 0)
647                                 ondemand = 1;
648                 }
649                 if(dev != nil && cons != nil)
650                         console(fs, cons, dev, speed, cronly, ondemand);
651                 ndbfree(t);
652         }
653 }
654
655 /*
656  *  a request processor (one per Fs)
657  */
658 void
659 fsrun(void *v)
660 {
661         int n, t;
662         Request *r;
663         Aux *f;
664         void **a = v;
665         Fs* fs;
666         int *pfd;
667
668         fs = a[0];
669         pfd = a[1];
670         fs->fd = pfd[0];
671         readdb(fs);
672         notify(handler);
673         for(;;){
674                 if(ndbchanged(db))
675                         readdb(fs);
676                 r = alloccreq(fs, messagesize);
677                 while((n = read9pmsg(fs->fd, r->buf, messagesize)) == 0)
678                         ;
679                 if(n < 0)
680                         fatal("unmounted");
681
682                 if(convM2S(r->buf, n, &r->f) == 0){
683                         fprint(2, "can't convert %ux %ux %ux\n", r->buf[0],
684                                 r->buf[1], r->buf[2]);
685                         free(r);
686                         continue;
687                 }
688
689
690                 f = fsgetfid(fs, r->f.fid);
691                 r->fid = f;
692                 if(debug)
693                         fprint(2, "%F path %llux\n", &r->f, f->qid.path);
694
695                 t = r->f.type;
696                 r->f.type++;
697                 (*fcall[t])(fs, r, f);
698         }
699 }
700
701 Aux*
702 fsgetfid(Fs *fs, int fid)
703 {
704         Aux *f, *nf;
705
706         lock(fs);
707         for(f = fs->hash[fid%Nhash]; f; f = f->next){
708                 if(f->fid == fid){
709                         f->ref++;
710                         unlock(fs);
711                         return f;
712                 }
713         }
714
715         nf = emalloc(sizeof(Aux));
716         nf->next = fs->hash[fid%Nhash];
717         fs->hash[fid%Nhash] = nf;
718         nf->fid = fid;
719         nf->ref = 1;
720         nf->wp = nf->buf;
721         nf->rp = nf->wp;
722         unlock(fs);
723         return nf;
724 }
725
726 void
727 fsputfid(Fs *fs, Aux *f)
728 {
729         Aux **l, *nf;
730
731         lock(fs);
732         if(--f->ref > 0){
733                 unlock(fs);
734                 return;
735         }
736         for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next)
737                 if(nf == f){
738                         *l = f->next;
739                         break;
740                 }
741         unlock(fs);
742         free(f->user);
743         free(f);
744 }
745
746 void
747 fsauth(Fs *fs, Request *r, Aux*)
748 {
749         fsreply(fs, r, "consolefs: authentication not required");
750 }
751
752 void
753 fsversion(Fs *fs, Request *r, Aux*)
754 {
755
756         if(r->f.msize < 256){
757                 fsreply(fs, r, "message size too small");
758                 return;
759         }
760         messagesize = r->f.msize;
761         if(messagesize > 8192+IOHDRSZ)
762                 messagesize = 8192+IOHDRSZ;
763         r->f.msize = messagesize;
764         if(strncmp(r->f.version, "9P2000", 6) != 0){
765                 fsreply(fs, r, "unrecognized 9P version");
766                 return;
767         }
768         r->f.version = "9P2000";
769
770         fsreply(fs, r, nil);
771 }
772
773 void
774 fsflush(Fs *fs, Request *r, Aux *f)
775 {
776         Request *or;
777
778         or = remtag(&f->r, r->f.oldtag);
779         if(or != nil){
780                 fsputfid(fs, or->fid);
781                 free(or);
782                 fsreply(fs, r, nil);
783         } else {
784                 fsputfid(fs, f);
785                 free(r);
786         }
787 }
788
789 void
790 fsattach(Fs *fs, Request *r, Aux *f)
791 {
792         f->qid.type = QTDIR;
793         f->qid.path = QID(0, Ttopdir);
794         f->qid.vers = 0;
795
796         if(r->f.uname[0])
797                 f->user = strdup(r->f.uname);
798         else
799                 f->user = strdup("none");
800
801         /* hold down the fid till the clunk */
802         f->attached = 1;
803         lock(fs);
804         f->ref++;
805         unlock(fs);
806
807         r->f.qid = f->qid;
808         fsreply(fs, r, nil);
809 }
810
811 void
812 fswalk(Fs *fs, Request *r, Aux *f)
813 {
814         char *name;
815         Dir d;
816         int i, n, nqid, nwname;
817         Qid qid, wqid[MAXWELEM];
818         Aux *nf;
819         char *err;
820
821         if(f->attached == 0){
822                 fsreply(fs, r, Enofid);
823                 return;
824         }
825
826         nf = nil;
827         if(r->f.fid != r->f.newfid){
828                 nf = fsgetfid(fs, r->f.newfid);
829                 nf->attached = f->attached;
830                 nf->open = f->open;
831                 nf->qid = f->qid;
832                 nf->user = strdup(f->user);
833                 nf->c = f->c;
834                 nf->wp = nf->buf;
835                 nf->rp = nf->wp;
836                 f = nf;
837         }
838
839         qid = f->qid;
840         err = nil;
841         nwname = r->f.nwname;
842         nqid = 0;
843         if(nwname > 0){
844                 for(; err == nil && nqid < nwname; nqid++){
845                         if(nqid >= MAXWELEM){
846                                 err = "too many name elements";
847                                 break;
848                         }
849                         name = r->f.wname[nqid];
850                         if(strcmp(name, "..") == 0)
851                                 qid = parentqid(qid);
852                         else if(strcmp(name, ".") != 0){
853                                 for(i = 0; ; i++){
854                                         n = fsdirgen(fs, qid, i, &d, nil, 0);
855                                         if(n < 0){
856                                                 err = Eexist;
857                                                 break;
858                                         }
859                                         if(n > 0 && strcmp(name, d.name) == 0){
860                                                 qid = d.qid;
861                                                 break;
862                                         }
863                                 }
864                         }
865                         wqid[nqid] = qid;
866                 }
867                 if(nf != nil && nqid < nwname)
868                         fsputfid(fs, nf);
869                 if(nqid == nwname)
870                         f->qid = qid;
871         }
872
873         memmove(r->f.wqid, wqid, nqid*sizeof(Qid));
874         r->f.nwqid = nqid;
875         fsreply(fs, r, err);
876 }
877
878 int
879 ingroup(char *user, char *group)
880 {
881         Ndbtuple *t, *nt;
882         Ndbs s;
883
884         t = ndbsearch(db, &s, "group", group);
885         if(t == nil)
886                 return 0;
887         for(nt = t; nt; nt = nt->entry){
888                 if(strcmp(nt->attr, "uid") == 0)
889                 if(strcmp(nt->val, user) == 0)
890                         break;
891         }
892         ndbfree(t);
893         return nt != nil;
894 }
895
896 int
897 userok(char *u, char *cname)
898 {
899         Ndbtuple *t, *nt;
900         Ndbs s;
901
902         t = ndbsearch(db, &s, "console", cname);
903         if(t == nil)
904                 return 0;
905
906         for(nt = t; nt; nt = nt->entry){
907                 if(strcmp(nt->attr, "uid") == 0)
908                 if(strcmp(nt->val, u) == 0)
909                         break;
910                 if(strcmp(nt->attr, "gid") == 0)
911                 if(ingroup(u, nt->val))
912                         break;
913         }
914         ndbfree(t);
915
916         return nt != nil;
917 }
918
919 int m2p[] ={
920         [OREAD]         4,
921         [OWRITE]        2,
922         [ORDWR]         6
923 };
924
925 /*
926  *  broadcast a message to all listeners
927  */
928 void
929 bcastmsg(Fs *fs, Console *c, char *msg, int n)
930 {
931         Aux *fl;
932
933         for(fl = c->flist; fl; fl = fl->cnext){
934                 fromconsole(fl, msg, n);
935                 fskick(fs, fl);
936         }
937 }
938
939 void
940 fsopen(Fs *fs, Request *r, Aux *f)
941 {
942         int mode;
943         Console *c;
944
945         if(f->attached == 0){
946                 fsreply(fs, r, Enofid);
947                 return;
948         }
949
950         if(f->open){
951                 fsreply(fs, r, Eisopen);
952                 return;
953         }
954
955         mode = r->f.mode & 3;
956
957         if((QTDIR & f->qid.type) && mode != OREAD){
958                 fsreply(fs, r, Eperm);
959                 return;
960         }
961
962         switch(TYPE(f->qid)){
963         case Qdata:
964                 c = fs->cons[CONS(f->qid)];
965                 if(!userok(f->user, c->name)){
966                         fsreply(fs, r, Eperm);
967                         return;
968                 }
969                 f->rp = f->buf;
970                 f->wp = f->buf;
971                 f->c = c;
972                 lock(c);
973                 sprint(f->mbuf, "[%s] ", f->user);
974                 f->bufn = strlen(f->mbuf);
975                 f->used = 0;
976                 f->cnext = c->flist;
977                 c->flist = f;
978                 bcastmembers(fs, c, "+", f);
979                 if(c->pid == 0)
980                         fsreopen(fs, c);
981                 unlock(c);
982                 break;
983         case Qctl:
984                 c = fs->cons[CONS(f->qid)];
985                 if(!userok(f->user, c->name)){
986                         fsreply(fs, r, Eperm);
987                         return;
988                 }
989                 f->c = c;
990                 break;
991         case Qstat:
992                 c = fs->cons[CONS(f->qid)];
993                 if(!userok(f->user, c->name)){
994                         fsreply(fs, r, Eperm);
995                         return;
996                 }
997                 f->c = c;
998                 break;
999         }
1000
1001         f->open = 1;
1002         r->f.iounit = messagesize-IOHDRSZ;
1003         r->f.qid = f->qid;
1004         fsreply(fs, r, nil);
1005 }
1006
1007 void
1008 fscreate(Fs *fs, Request *r, Aux*)
1009 {
1010         fsreply(fs, r, Eperm);
1011 }
1012
1013 void
1014 fsread(Fs *fs, Request *r, Aux *f)
1015 {
1016         uchar *p, *e;
1017         int i, m, off;
1018         vlong offset;
1019         Dir d;
1020         char sbuf[ERRMAX];
1021
1022         if(f->attached == 0){
1023                 fsreply(fs, r, Enofid);
1024                 return;
1025         }
1026
1027         if((int)r->f.count < 0){
1028                 fsreply(fs, r, Ebadcount);
1029                 return;
1030         }
1031
1032         if(QTDIR & f->qid.type){
1033                 p = r->buf + IOHDRSZ;
1034                 e = p + r->f.count;
1035                 offset = r->f.offset;
1036                 off = 0;
1037                 for(i=0; p<e; i++, off+=m){
1038                         m = fsdirgen(fs, f->qid, i, &d, p, e-p);
1039                         if(m < 0)
1040                                 break;
1041                         if(m > BIT16SZ && off >= offset)
1042                                 p += m;
1043                 }
1044                 r->f.data = (char*)r->buf + IOHDRSZ;
1045                 r->f.count = (char*)p - r->f.data;
1046         } else {
1047                 switch(TYPE(f->qid)){
1048                 case Qdata:
1049                         addreq(&f->r, r);
1050                         fskick(fs, f);
1051                         return;
1052                 case Qctl:
1053                         r->f.data = (char*)r->buf+IOHDRSZ;
1054                         r->f.count = 0;
1055                         break;
1056                 case Qstat:
1057                         if(r->f.count > sizeof(sbuf))
1058                                 r->f.count = sizeof(sbuf);
1059                         i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset);
1060                         if(i < 0){
1061                                 errstr(sbuf, sizeof sbuf);
1062                                 fsreply(fs, r, sbuf);
1063                                 return;
1064                         }
1065                         r->f.data = sbuf;
1066                         r->f.count = i;
1067                         break;
1068                 default:
1069                         fsreply(fs, r, Eexist);
1070                         return;
1071                 }
1072         }
1073         fsreply(fs, r, nil);
1074 }
1075
1076 void
1077 fswrite(Fs *fs, Request *r, Aux *f)
1078 {
1079         int i, eol = 0;
1080
1081         if(f->attached == 0){
1082                 fsreply(fs, r, Enofid);
1083                 return;
1084         }
1085
1086         if((int)r->f.count < 0){
1087                 fsreply(fs, r, Ebadcount);
1088                 return;
1089         }
1090
1091         if(QTDIR & f->qid.type){
1092                 fsreply(fs, r, Eperm);
1093                 return;
1094         }
1095
1096         switch(TYPE(f->qid)){
1097         default:
1098                 fsreply(fs, r, Eperm);
1099                 return;
1100         case Qctl:
1101                 write(f->c->cfd, r->f.data, r->f.count);
1102                 break;
1103         case Qdata:
1104                 for(i = 0; i < r->f.count; i++){
1105                         if(r->f.data[i] == '\n'){
1106                                 if(f->c->chat && f->used)
1107                                         eol = 1;
1108                                 if(f->c->cronly)
1109                                         r->f.data[i] = '\r';
1110                         }
1111                         else
1112                                 f->used = 1;
1113                 }
1114                 if(f->c->chat){
1115                         fskick(fs, f);
1116                         if(!f->used)
1117                                 break;
1118         
1119                         if(f->bufn + r->f.count > Bufsize){
1120                                 r->f.count -= (f->bufn + r->f.count) % Bufsize;
1121                                 eol = 1;
1122                         }
1123                         strncat(f->mbuf, r->f.data, r->f.count);
1124                         f->bufn += r->f.count;
1125                         if(eol){
1126                                 bcastmsg(fs, f->c, f->mbuf, f->bufn);
1127                                 sprint(f->mbuf, "[%s] ", f->user);
1128                                 f->bufn = strlen(f->mbuf);
1129                                 f->used = 0;
1130                         }
1131                 }
1132                 else
1133                         write(f->c->fd, r->f.data, r->f.count);
1134                 break;
1135         }
1136         fsreply(fs, r, nil);
1137 }
1138
1139 void
1140 fsclunk(Fs *fs, Request *r, Aux *f)
1141 {
1142         Aux **l, *fl;
1143         Request *nr;
1144
1145         if(f->open && TYPE(f->qid) == Qdata){
1146                 while((nr = remreq(&f->r)) != nil){
1147                         fsputfid(fs, f);
1148                         free(nr);
1149                 }
1150
1151                 lock(f->c);
1152                 for(l = &f->c->flist; *l; l = &fl->cnext){
1153                         fl = *l;
1154                         if(fl == f){
1155                                 *l = fl->cnext;
1156                                 break;
1157                         }
1158                 }
1159                 bcastmembers(fs, f->c, "-", f);
1160                 if(f->c->ondemand && f->c->flist == nil)
1161                         fsreopen(fs, f->c);
1162                 unlock(f->c);
1163         }
1164         fsreply(fs, r, nil);
1165         fsputfid(fs, f);
1166 }
1167
1168 void
1169 fsremove(Fs *fs, Request *r, Aux*)
1170 {
1171         fsreply(fs, r, Eperm);
1172 }
1173
1174 void
1175 fsstat(Fs *fs, Request *r, Aux *f)
1176 {
1177         int i, n;
1178         Qid q;
1179         Dir d;
1180
1181         q = parentqid(f->qid);
1182         for(i = 0; ; i++){
1183                 r->f.stat = r->buf+IOHDRSZ;
1184                 n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ);
1185                 if(n < 0){
1186                         fsreply(fs, r, Eexist);
1187                         return;
1188                 }
1189                 r->f.nstat = n;
1190                 if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path)
1191                         break;
1192         }
1193         fsreply(fs, r, nil);
1194 }
1195
1196 void
1197 fswstat(Fs *fs, Request *r, Aux*)
1198 {
1199         fsreply(fs, r, Eperm);
1200 }
1201
1202 void
1203 fsreply(Fs *fs, Request *r, char *err)
1204 {
1205         int n;
1206         uchar buf[8192+IOHDRSZ];
1207
1208         if(err){
1209                 r->f.type = Rerror;
1210                 r->f.ename = err;
1211         }
1212         n = convS2M(&r->f, buf, messagesize);
1213         if(debug)
1214                 fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n);
1215         fsputfid(fs, r->fid);
1216         if(write(fs->fd, buf, n) != n)
1217                 fatal("unmounted");
1218         free(r);
1219 }
1220
1221 /*
1222  *  called whenever input or a read request has been received
1223  */
1224 void
1225 fskick(Fs *fs, Aux *f)
1226 {
1227         Request *r;
1228         char *p, *rp, *wp, *ep;
1229         int i;
1230
1231         lock(f);
1232         while(f->rp != f->wp){
1233                 r = remreq(&f->r);
1234                 if(r == nil)
1235                         break;
1236                 p = (char*)r->buf;
1237                 rp = f->rp;
1238                 wp = f->wp;
1239                 ep = &f->buf[Bufsize];
1240                 for(i = 0; i < r->f.count && rp != wp; i++){
1241                         *p++ = *rp++;
1242                         if(rp >= ep)
1243                                 rp = f->buf;
1244                 }
1245                 f->rp = rp;
1246                 r->f.data = (char*)r->buf;
1247                 r->f.count = p - (char*)r->buf;
1248                 fsreply(fs, r, nil);
1249         }
1250         unlock(f);
1251 }
1252
1253 void
1254 usage(void)
1255 {
1256         fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n");
1257         threadexitsall("usage");
1258 }
1259
1260 void
1261 threadmain(int argc, char **argv)
1262 {
1263         fmtinstall('F', fcallfmt);
1264
1265         ARGBEGIN{
1266         case 'd':
1267                 debug++;
1268                 break;
1269         case 'c':
1270                 consoledb = ARGF();
1271                 if(consoledb == nil)
1272                         usage();
1273                 break;
1274         case 'm':
1275                 mntpt = ARGF();
1276                 if(mntpt == nil)
1277                         usage();
1278                 break;
1279         }ARGEND;
1280
1281         db = ndbopen(consoledb);
1282         if(db == nil)
1283                 fatal("can't open %s: %r", consoledb);
1284
1285         fsmount(mntpt);
1286 }