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