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
fatal(char * fmt,...)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*
emalloc(uint n)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*
allocreq(Fs * fs,int bufsize)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
addreq(Reqlist * l,Request * r)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*
remreq(Reqlist * l)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*
remtag(Reqlist * l,int tag)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
parentqid(Qid q)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
fsdirgen(Fs * fs,Qid parent,int i,Dir * d,uchar * buf,int nbuf)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*
fsmount(char * mntpt)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
fsreopen(Fs * fs,Console * c)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
change(Fs * fs,Console * c,int doreopen,int speed,int cronly,int ondemand)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
console(Fs * fs,char * name,char * dev,int speed,int cronly,int ondemand)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
fromconsole(Fid * f,char * p,int n)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
bcastmembers(Fs * fs,Console * c,char * msg,Fid * f)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
handler(void *,char * msg)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
fsreader(void * v)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
readdb(Fs * fs)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
fsrun(void * v)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 n = read9pmsg(fs->fd, r->buf, messagesize);
685 if(n <= 0)
686 fatal("unmounted");
687
688 if(convM2S(r->buf, n, &r->f) == 0){
689 fprint(2, "can't convert %ux %ux %ux\n", r->buf[0],
690 r->buf[1], r->buf[2]);
691 free(r);
692 continue;
693 }
694
695
696 f = fsgetfid(fs, r->f.fid);
697 r->fid = f;
698 if(debug)
699 fprint(2, "%F path %llux\n", &r->f, f->qid.path);
700
701 t = r->f.type;
702 r->f.type++;
703 (*fcall[t])(fs, r, f);
704 }
705 }
706
707 Fid*
fsgetfid(Fs * fs,int fid)708 fsgetfid(Fs *fs, int fid)
709 {
710 Fid *f, *nf;
711
712 lock(fs);
713 for(f = fs->hash[fid%Nhash]; f; f = f->next){
714 if(f->fid == fid){
715 f->ref++;
716 unlock(fs);
717 return f;
718 }
719 }
720
721 nf = emalloc(sizeof(Fid));
722 nf->next = fs->hash[fid%Nhash];
723 fs->hash[fid%Nhash] = nf;
724 nf->fid = fid;
725 nf->ref = 1;
726 nf->wp = nf->buf;
727 nf->rp = nf->wp;
728 unlock(fs);
729 return nf;
730 }
731
732 void
fsputfid(Fs * fs,Fid * f)733 fsputfid(Fs *fs, Fid *f)
734 {
735 Fid **l, *nf;
736
737 lock(fs);
738 if(--f->ref > 0){
739 unlock(fs);
740 return;
741 }
742 for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next)
743 if(nf == f){
744 *l = f->next;
745 break;
746 }
747 unlock(fs);
748 free(f->user);
749 free(f);
750 }
751
752 void
fsauth(Fs * fs,Request * r,Fid *)753 fsauth(Fs *fs, Request *r, Fid*)
754 {
755 fsreply(fs, r, "consolefs: authentication not required");
756 }
757
758 void
fsversion(Fs * fs,Request * r,Fid *)759 fsversion(Fs *fs, Request *r, Fid*)
760 {
761
762 if(r->f.msize < 256){
763 fsreply(fs, r, "message size too small");
764 return;
765 }
766 messagesize = r->f.msize;
767 if(messagesize > 8192+IOHDRSZ)
768 messagesize = 8192+IOHDRSZ;
769 r->f.msize = messagesize;
770 if(strncmp(r->f.version, "9P2000", 6) != 0){
771 fsreply(fs, r, "unrecognized 9P version");
772 return;
773 }
774 r->f.version = "9P2000";
775
776 fsreply(fs, r, nil);
777 }
778
779 void
fsflush(Fs * fs,Request * r,Fid * f)780 fsflush(Fs *fs, Request *r, Fid *f)
781 {
782 Request *or;
783
784 or = remtag(&f->r, r->f.oldtag);
785 if(or != nil){
786 fsputfid(fs, or->fid);
787 free(or);
788 }
789 fsreply(fs, r, nil);
790 }
791
792 void
fsattach(Fs * fs,Request * r,Fid * f)793 fsattach(Fs *fs, Request *r, Fid *f)
794 {
795 f->qid.type = QTDIR;
796 f->qid.path = QID(0, Ttopdir);
797 f->qid.vers = 0;
798
799 if(r->f.uname[0])
800 f->user = strdup(r->f.uname);
801 else
802 f->user = strdup("none");
803
804 /* hold down the fid till the clunk */
805 f->attached = 1;
806 lock(fs);
807 f->ref++;
808 unlock(fs);
809
810 r->f.qid = f->qid;
811 fsreply(fs, r, nil);
812 }
813
814 void
fswalk(Fs * fs,Request * r,Fid * f)815 fswalk(Fs *fs, Request *r, Fid *f)
816 {
817 char *name;
818 Dir d;
819 int i, n, nqid, nwname;
820 Qid qid, wqid[MAXWELEM];
821 Fid *nf;
822 char *err;
823
824 if(f->attached == 0){
825 fsreply(fs, r, Enofid);
826 return;
827 }
828
829 nf = nil;
830 if(r->f.fid != r->f.newfid){
831 nf = fsgetfid(fs, r->f.newfid);
832 nf->attached = f->attached;
833 nf->open = f->open;
834 nf->qid = f->qid;
835 nf->user = strdup(f->user);
836 nf->c = f->c;
837 nf->wp = nf->buf;
838 nf->rp = nf->wp;
839 f = nf;
840 }
841
842 qid = f->qid;
843 err = nil;
844 nwname = r->f.nwname;
845 nqid = 0;
846 if(nwname > 0){
847 for(; err == nil && nqid < nwname; nqid++){
848 if(nqid >= MAXWELEM){
849 err = "too many name elements";
850 break;
851 }
852 name = r->f.wname[nqid];
853 if(strcmp(name, "..") == 0)
854 qid = parentqid(qid);
855 else if(strcmp(name, ".") != 0){
856 for(i = 0; ; i++){
857 n = fsdirgen(fs, qid, i, &d, nil, 0);
858 if(n < 0){
859 err = Eexist;
860 break;
861 }
862 if(n > 0 && strcmp(name, d.name) == 0){
863 qid = d.qid;
864 break;
865 }
866 }
867 }
868 wqid[nqid] = qid;
869 }
870 if(nf != nil && nqid < nwname)
871 fsputfid(fs, nf);
872 if(nqid == nwname)
873 f->qid = qid;
874 }
875
876 memmove(r->f.wqid, wqid, nqid*sizeof(Qid));
877 r->f.nwqid = nqid;
878 fsreply(fs, r, err);
879 }
880
881 int
ingroup(char * user,char * group)882 ingroup(char *user, char *group)
883 {
884 Ndbtuple *t, *nt;
885 Ndbs s;
886
887 t = ndbsearch(db, &s, "group", group);
888 if(t == nil)
889 return 0;
890 for(nt = t; nt; nt = nt->entry){
891 if(strcmp(nt->attr, "uid") == 0)
892 if(strcmp(nt->val, user) == 0)
893 break;
894 }
895 ndbfree(t);
896 return nt != nil;
897 }
898
899 int
userok(char * u,char * cname)900 userok(char *u, char *cname)
901 {
902 Ndbtuple *t, *nt;
903 Ndbs s;
904
905 t = ndbsearch(db, &s, "console", cname);
906 if(t == nil)
907 return 0;
908
909 for(nt = t; nt; nt = nt->entry){
910 if(strcmp(nt->attr, "uid") == 0)
911 if(strcmp(nt->val, u) == 0)
912 break;
913 if(strcmp(nt->attr, "gid") == 0)
914 if(ingroup(u, nt->val))
915 break;
916 }
917 ndbfree(t);
918
919 return nt != nil;
920 }
921
922 int m2p[] ={
923 [OREAD] 4,
924 [OWRITE] 2,
925 [ORDWR] 6
926 };
927
928 /*
929 * broadcast a message to all listeners
930 */
931 void
bcastmsg(Fs * fs,Console * c,char * msg,int n)932 bcastmsg(Fs *fs, Console *c, char *msg, int n)
933 {
934 Fid *fl;
935
936 for(fl = c->flist; fl; fl = fl->cnext){
937 fromconsole(fl, msg, n);
938 fskick(fs, fl);
939 }
940 }
941
942 void
fsopen(Fs * fs,Request * r,Fid * f)943 fsopen(Fs *fs, Request *r, Fid *f)
944 {
945 int mode;
946 Console *c;
947
948 if(f->attached == 0){
949 fsreply(fs, r, Enofid);
950 return;
951 }
952
953 if(f->open){
954 fsreply(fs, r, Eisopen);
955 return;
956 }
957
958 mode = r->f.mode & 3;
959
960 if((QTDIR & f->qid.type) && mode != OREAD){
961 fsreply(fs, r, Eperm);
962 return;
963 }
964
965 switch(TYPE(f->qid)){
966 case Qdata:
967 c = fs->cons[CONS(f->qid)];
968 if(!userok(f->user, c->name)){
969 fsreply(fs, r, Eperm);
970 return;
971 }
972 f->rp = f->buf;
973 f->wp = f->buf;
974 f->c = c;
975 lock(c);
976 sprint(f->mbuf, "[%s] ", f->user);
977 f->bufn = strlen(f->mbuf);
978 f->used = 0;
979 f->cnext = c->flist;
980 c->flist = f;
981 bcastmembers(fs, c, "+", f);
982 if(c->pid == 0)
983 fsreopen(fs, c);
984 unlock(c);
985 break;
986 case Qctl:
987 c = fs->cons[CONS(f->qid)];
988 if(!userok(f->user, c->name)){
989 fsreply(fs, r, Eperm);
990 return;
991 }
992 f->c = c;
993 break;
994 case Qstat:
995 c = fs->cons[CONS(f->qid)];
996 if(!userok(f->user, c->name)){
997 fsreply(fs, r, Eperm);
998 return;
999 }
1000 f->c = c;
1001 break;
1002 }
1003
1004 f->open = 1;
1005 r->f.iounit = messagesize-IOHDRSZ;
1006 r->f.qid = f->qid;
1007 fsreply(fs, r, nil);
1008 }
1009
1010 void
fscreate(Fs * fs,Request * r,Fid *)1011 fscreate(Fs *fs, Request *r, Fid*)
1012 {
1013 fsreply(fs, r, Eperm);
1014 }
1015
1016 void
fsread(Fs * fs,Request * r,Fid * f)1017 fsread(Fs *fs, Request *r, Fid *f)
1018 {
1019 uchar *p, *e;
1020 int i, m, off;
1021 vlong offset;
1022 Dir d;
1023 char sbuf[ERRMAX];
1024
1025 if(f->attached == 0){
1026 fsreply(fs, r, Enofid);
1027 return;
1028 }
1029
1030 if((int)r->f.count < 0){
1031 fsreply(fs, r, Ebadcount);
1032 return;
1033 }
1034
1035 if(QTDIR & f->qid.type){
1036 p = r->buf + IOHDRSZ;
1037 e = p + r->f.count;
1038 offset = r->f.offset;
1039 off = 0;
1040 for(i=0; p<e; i++, off+=m){
1041 m = fsdirgen(fs, f->qid, i, &d, p, e-p);
1042 if(m < 0)
1043 break;
1044 if(m > BIT16SZ && off >= offset)
1045 p += m;
1046 }
1047 r->f.data = (char*)r->buf + IOHDRSZ;
1048 r->f.count = (char*)p - r->f.data;
1049 } else {
1050 switch(TYPE(f->qid)){
1051 case Qdata:
1052 addreq(&f->r, r);
1053 fskick(fs, f);
1054 return;
1055 case Qctl:
1056 r->f.data = (char*)r->buf+IOHDRSZ;
1057 r->f.count = 0;
1058 break;
1059 case Qstat:
1060 if(r->f.count > sizeof(sbuf))
1061 r->f.count = sizeof(sbuf);
1062 i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset);
1063 if(i < 0){
1064 errstr(sbuf, sizeof sbuf);
1065 fsreply(fs, r, sbuf);
1066 return;
1067 }
1068 r->f.data = sbuf;
1069 r->f.count = i;
1070 break;
1071 default:
1072 fsreply(fs, r, Eexist);
1073 return;
1074 }
1075 }
1076 fsreply(fs, r, nil);
1077 }
1078
1079 void
fswrite(Fs * fs,Request * r,Fid * f)1080 fswrite(Fs *fs, Request *r, Fid *f)
1081 {
1082 int i, eol = 0;
1083
1084 if(f->attached == 0){
1085 fsreply(fs, r, Enofid);
1086 return;
1087 }
1088
1089 if((int)r->f.count < 0){
1090 fsreply(fs, r, Ebadcount);
1091 return;
1092 }
1093
1094 if(QTDIR & f->qid.type){
1095 fsreply(fs, r, Eperm);
1096 return;
1097 }
1098
1099 switch(TYPE(f->qid)){
1100 default:
1101 fsreply(fs, r, Eperm);
1102 return;
1103 case Qctl:
1104 write(f->c->cfd, r->f.data, r->f.count);
1105 break;
1106 case Qdata:
1107 for(i = 0; i < r->f.count; i++){
1108 if(r->f.data[i] == '\n'){
1109 if(f->c->chat && f->used)
1110 eol = 1;
1111 if(f->c->cronly)
1112 r->f.data[i] = '\r';
1113 }
1114 else
1115 f->used = 1;
1116 }
1117 if(f->c->chat){
1118 fskick(fs, f);
1119 if(!f->used)
1120 break;
1121
1122 if(f->bufn + r->f.count > Bufsize){
1123 r->f.count -= (f->bufn + r->f.count) % Bufsize;
1124 eol = 1;
1125 }
1126 strncat(f->mbuf, r->f.data, r->f.count);
1127 f->bufn += r->f.count;
1128 if(eol){
1129 bcastmsg(fs, f->c, f->mbuf, f->bufn);
1130 sprint(f->mbuf, "[%s] ", f->user);
1131 f->bufn = strlen(f->mbuf);
1132 f->used = 0;
1133 }
1134 }
1135 else
1136 write(f->c->fd, r->f.data, r->f.count);
1137 break;
1138 }
1139 fsreply(fs, r, nil);
1140 }
1141
1142 void
fsclunk(Fs * fs,Request * r,Fid * f)1143 fsclunk(Fs *fs, Request *r, Fid *f)
1144 {
1145 Fid **l, *fl;
1146 Request *nr;
1147
1148 if(f->open && TYPE(f->qid) == Qdata){
1149 while((nr = remreq(&f->r)) != nil){
1150 fsputfid(fs, f);
1151 free(nr);
1152 }
1153
1154 lock(f->c);
1155 for(l = &f->c->flist; *l; l = &fl->cnext){
1156 fl = *l;
1157 if(fl == f){
1158 *l = fl->cnext;
1159 break;
1160 }
1161 }
1162 bcastmembers(fs, f->c, "-", f);
1163 if(f->c->ondemand && f->c->flist == nil)
1164 fsreopen(fs, f->c);
1165 unlock(f->c);
1166 }
1167 fsreply(fs, r, nil);
1168 fsputfid(fs, f);
1169 }
1170
1171 void
fsremove(Fs * fs,Request * r,Fid *)1172 fsremove(Fs *fs, Request *r, Fid*)
1173 {
1174 fsreply(fs, r, Eperm);
1175 }
1176
1177 void
fsstat(Fs * fs,Request * r,Fid * f)1178 fsstat(Fs *fs, Request *r, Fid *f)
1179 {
1180 int i, n;
1181 Qid q;
1182 Dir d;
1183
1184 q = parentqid(f->qid);
1185 for(i = 0; ; i++){
1186 r->f.stat = r->buf+IOHDRSZ;
1187 n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ);
1188 if(n < 0){
1189 fsreply(fs, r, Eexist);
1190 return;
1191 }
1192 r->f.nstat = n;
1193 if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path)
1194 break;
1195 }
1196 fsreply(fs, r, nil);
1197 }
1198
1199 void
fswstat(Fs * fs,Request * r,Fid *)1200 fswstat(Fs *fs, Request *r, Fid*)
1201 {
1202 fsreply(fs, r, Eperm);
1203 }
1204
1205 void
fsreply(Fs * fs,Request * r,char * err)1206 fsreply(Fs *fs, Request *r, char *err)
1207 {
1208 int n;
1209 uchar buf[8192+IOHDRSZ];
1210
1211 if(err){
1212 r->f.type = Rerror;
1213 r->f.ename = err;
1214 }
1215 n = convS2M(&r->f, buf, messagesize);
1216 if(debug)
1217 fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n);
1218 fsputfid(fs, r->fid);
1219 if(write(fs->fd, buf, n) != n)
1220 fatal("unmounted");
1221 free(r);
1222 }
1223
1224 /*
1225 * called whenever input or a read request has been received
1226 */
1227 void
fskick(Fs * fs,Fid * f)1228 fskick(Fs *fs, Fid *f)
1229 {
1230 Request *r;
1231 char *p, *rp, *wp, *ep;
1232 int i;
1233
1234 lock(f);
1235 while(f->rp != f->wp){
1236 r = remreq(&f->r);
1237 if(r == nil)
1238 break;
1239 p = (char*)r->buf;
1240 rp = f->rp;
1241 wp = f->wp;
1242 ep = &f->buf[Bufsize];
1243 for(i = 0; i < r->f.count && rp != wp; i++){
1244 *p++ = *rp++;
1245 if(rp >= ep)
1246 rp = f->buf;
1247 }
1248 f->rp = rp;
1249 r->f.data = (char*)r->buf;
1250 r->f.count = p - (char*)r->buf;
1251 fsreply(fs, r, nil);
1252 }
1253 unlock(f);
1254 }
1255
1256 void
usage(void)1257 usage(void)
1258 {
1259 fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n");
1260 threadexitsall("usage");
1261 }
1262
1263 void
threadmain(int argc,char ** argv)1264 threadmain(int argc, char **argv)
1265 {
1266 rfork(RFNOTEG);
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 }
1291