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