xref: /plan9/sys/src/cmd/aux/consolefs.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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 	fs->ncons++;
484 	c->name = strdup(name);
485 	c->dev = strdup(dev);
486 	c->fd = -1;
487 	c->cfd = -1;
488 	c->sfd = -1;
489 	change(fs, c, 1, speed, cronly, ondemand);
490 }
491 
492 /*
493  *  buffer data from console to a client.
494  *  circular q with writer able to catch up to reader.
495  *  the reader may miss data but always sees an in order sequence.
496  */
497 void
498 fromconsole(Fid *f, char *p, int n)
499 {
500 	char *rp, *wp, *ep;
501 	int pass;
502 
503 	lock(f);
504 	rp = f->rp;
505 	wp = f->wp;
506 	ep = f->buf + sizeof(f->buf);
507 	pass = 0;
508 	while(n--){
509 		*wp++ = *p++;
510 		if(wp >= ep)
511 			wp = f->buf;
512 		if(rp == wp)
513 			pass = 1;
514 	}
515 	f->wp = wp;
516 
517 	/*  we overtook the read pointer, push it up so readers always
518 	 *  see the tail of what was written
519 	 */
520 	if(pass){
521 		wp++;
522 		if(wp >= ep)
523 			f->rp = f->buf;
524 		else
525 			f->rp = wp;
526 	}
527 	unlock(f);
528 }
529 
530 /*
531  *  broadcast a list of members to all listeners
532  */
533 void
534 bcastmembers(Fs *fs, Console *c, char *msg, Fid *f)
535 {
536 	int n;
537 	Fid *fl;
538 	char buf[512];
539 
540 	sprint(buf, "[%s%s", msg, f->user);
541 	for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){
542 		if(f == fl)
543 			continue;
544 		strcat(buf, ", ");
545 		strcat(buf, fl->user);
546 	}
547 	strcat(buf, "]\n");
548 
549 	n = strlen(buf);
550 	for(fl = c->flist; fl; fl = fl->cnext){
551 		fromconsole(fl, buf, n);
552 		fskick(fs, fl);
553 	}
554 }
555 
556 void
557 handler(void*, char *msg)
558 {
559 	if(strstr(msg, "reopen"))
560 		noted(NCONT);
561 	noted(NDFLT);
562 }
563 
564 
565 /*
566  *  a process to read console output and broadcast it (one per console)
567  */
568 void
569 fsreader(void *v)
570 {
571 	int n;
572 	Fid *fl;
573 	char buf[1024];
574 	Fs *fs;
575 	Console *c;
576 	void **a;
577 
578 	a = v;
579 	fs = a[0];
580 	c = a[1];
581 	c->pid = getpid();
582 	notify(handler);
583 	for(;;){
584 		n = read(c->fd, buf, sizeof(buf));
585 		if(n < 0)
586 			break;
587 		lock(c);
588 		for(fl = c->flist; fl; fl = fl->cnext){
589 			fromconsole(fl, buf, n);
590 			fskick(fs, fl);
591 		}
592 		unlock(c);
593 	}
594 }
595 
596 void
597 readdb(Fs *fs)
598 {
599 	Ndbtuple *t, *nt;
600 	char *dev, *cons;
601 	int cronly, speed, ondemand;
602 
603 	ndbreopen(db);
604 
605 	/* start a listener for each console */
606 	for(;;){
607 		t = ndbparse(db);
608 		if(t == nil)
609 			break;
610 		dev = nil;
611 		cons = nil;
612 		speed = 9600;
613 		cronly = 0;
614 		ondemand = 0;
615 		for(nt = t; nt; nt = nt->entry){
616 			if(strcmp(nt->attr, "console") == 0)
617 				cons = nt->val;
618 			else if(strcmp(nt->attr, "dev") == 0)
619 				dev = nt->val;
620 			else if(strcmp(nt->attr, "speed") == 0)
621 				speed = atoi(nt->val);
622 			else if(strcmp(nt->attr, "cronly") == 0)
623 				cronly = 1;
624 			else if(strcmp(nt->attr, "openondemand") == 0)
625 				ondemand = 1;
626 		}
627 		if(dev != nil && cons != nil)
628 			console(fs, cons, dev, speed, cronly, ondemand);
629 		ndbfree(t);
630 	}
631 }
632 
633 int dbmtime;
634 
635 /*
636  *  a request processor (one per Fs)
637  */
638 void
639 fsrun(void *v)
640 {
641 	int n, t;
642 	Request *r;
643 	Fid *f;
644 	Dir *d;
645 	void **a = v;
646 	Fs* fs;
647 	int *pfd;
648 
649 	fs = a[0];
650 	pfd = a[1];
651 	fs->fd = pfd[0];
652 	for(;;){
653 		d = dirstat(consoledb);
654 		if(d != nil && d->mtime != dbmtime){
655 			dbmtime = d->mtime;
656 			readdb(fs);
657 		}
658 		free(d);
659 		r = allocreq(fs, messagesize);
660 		n = read9pmsg(fs->fd, r->buf, messagesize);
661 		if(n <= 0)
662 			fatal("unmounted");
663 
664 		if(convM2S(r->buf, n, &r->f) == 0){
665 			fprint(2, "can't convert %ux %ux %ux\n", r->buf[0],
666 				r->buf[1], r->buf[2]);
667 			free(r);
668 			continue;
669 		}
670 
671 
672 		f = fsgetfid(fs, r->f.fid);
673 		r->fid = f;
674 		if(debug)
675 			fprint(2, "%F path %llux\n", &r->f, f->qid.path);
676 
677 		t = r->f.type;
678 		r->f.type++;
679 		(*fcall[t])(fs, r, f);
680 	}
681 }
682 
683 Fid*
684 fsgetfid(Fs *fs, int fid)
685 {
686 	Fid *f, *nf;
687 
688 	lock(fs);
689 	for(f = fs->hash[fid%Nhash]; f; f = f->next){
690 		if(f->fid == fid){
691 			f->ref++;
692 			unlock(fs);
693 			return f;
694 		}
695 	}
696 
697 	nf = emalloc(sizeof(Fid));
698 	nf->next = fs->hash[fid%Nhash];
699 	fs->hash[fid%Nhash] = nf;
700 	nf->fid = fid;
701 	nf->ref = 1;
702 	nf->wp = nf->buf;
703 	nf->rp = nf->wp;
704 	unlock(fs);
705 	return nf;
706 }
707 
708 void
709 fsputfid(Fs *fs, Fid *f)
710 {
711 	Fid **l, *nf;
712 
713 	lock(fs);
714 	if(--f->ref > 0){
715 		unlock(fs);
716 		return;
717 	}
718 	for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next)
719 		if(nf == f){
720 			*l = f->next;
721 			break;
722 		}
723 	unlock(fs);
724 	free(f->user);
725 	free(f);
726 }
727 
728 
729 void
730 fsauth(Fs *fs, Request *r, Fid*)
731 {
732 	fsreply(fs, r, "consolefs: authentication not required");
733 }
734 
735 void
736 fsversion(Fs *fs, Request *r, Fid*)
737 {
738 
739 	if(r->f.msize < 256){
740 		fsreply(fs, r, "message size too small");
741 		return;
742 	}
743 	messagesize = r->f.msize;
744 	if(messagesize > 8192+IOHDRSZ)
745 		messagesize = 8192+IOHDRSZ;
746 	r->f.msize = messagesize;
747 	if(strncmp(r->f.version, "9P2000", 6) != 0){
748 		fsreply(fs, r, "unrecognized 9P version");
749 		return;
750 	}
751 	r->f.version = "9P2000";
752 
753 	fsreply(fs, r, nil);
754 }
755 
756 void
757 fsflush(Fs *fs, Request *r, Fid *f)
758 {
759 	Request *or;
760 
761 	or = remtag(&f->r, r->f.oldtag);
762 	if(or != nil){
763 		fsputfid(fs, or->fid);
764 		free(or);
765 	}
766 	fsreply(fs, r, nil);
767 }
768 
769 void
770 fsattach(Fs *fs, Request *r, Fid *f)
771 {
772 	f->qid.type = QTDIR;
773 	f->qid.path = QID(0, Ttopdir);
774 	f->qid.vers = 0;
775 
776 	if(r->f.uname[0])
777 		f->user = strdup(r->f.uname);
778 	else
779 		f->user = strdup("none");
780 
781 	/* hold down the fid till the clunk */
782 	f->attached = 1;
783 	lock(fs);
784 	f->ref++;
785 	unlock(fs);
786 
787 	r->f.qid = f->qid;
788 	fsreply(fs, r, nil);
789 }
790 
791 void
792 fswalk(Fs *fs, Request *r, Fid *f)
793 {
794 	char *name;
795 	Dir d;
796 	int i, n, nqid, nwname;
797 	Qid qid, wqid[MAXWELEM];
798 	Fid *nf;
799 	char *err;
800 
801 	if(f->attached == 0){
802 		fsreply(fs, r, Enofid);
803 		return;
804 	}
805 
806 	nf = nil;
807 	if(r->f.fid != r->f.newfid){
808 		nf = fsgetfid(fs, r->f.newfid);
809 		nf->attached = f->attached;
810 		nf->open = f->open;
811 		nf->qid = f->qid;
812 		nf->user = strdup(f->user);
813 		nf->c = f->c;
814 		nf->wp = nf->buf;
815 		nf->rp = nf->wp;
816 		f = nf;
817 	}
818 
819 	qid = f->qid;
820 	err = nil;
821 	nwname = r->f.nwname;
822 	nqid = 0;
823 	if(nwname > 0){
824 		for(; err == nil && nqid < nwname; nqid++){
825 			if(nqid >= MAXWELEM){
826 				err = "too many name elements";
827 				break;
828 			}
829 			name = r->f.wname[nqid];
830 			if(strcmp(name, "..") == 0)
831 				qid = parentqid(qid);
832 			else if(strcmp(name, ".") != 0){
833 				for(i = 0; ; i++){
834 					n = fsdirgen(fs, qid, i, &d, nil, 0);
835 					if(n < 0){
836 						err = Eexist;
837 						break;
838 					}
839 					if(n > 0 && strcmp(name, d.name) == 0){
840 						qid = d.qid;
841 						break;
842 					}
843 				}
844 			}
845 			wqid[nqid] = qid;
846 		}
847 		if(nf != nil && nqid < nwname)
848 			fsputfid(fs, nf);
849 		if(nqid == nwname)
850 			f->qid = qid;
851 	}
852 
853 	memmove(r->f.wqid, wqid, nqid*sizeof(Qid));
854 	r->f.nwqid = nqid;
855 	fsreply(fs, r, err);
856 }
857 
858 int
859 ingroup(char *user, char *group)
860 {
861 	Ndbtuple *t, *nt;
862 	Ndbs s;
863 
864 	t = ndbsearch(db, &s, "group", group);
865 	if(t == nil)
866 		return 0;
867 	for(nt = t; nt; nt = nt->entry){
868 		if(strcmp(nt->attr, "uid") == 0)
869 		if(strcmp(nt->val, user) == 0)
870 			break;
871 	}
872 	ndbfree(t);
873 	return nt != nil;
874 }
875 
876 int
877 userok(char *u, char *cname)
878 {
879 	Ndbtuple *t, *nt;
880 	Ndbs s;
881 
882 	t = ndbsearch(db, &s, "console", cname);
883 	if(t == nil)
884 		return 0;
885 
886 	for(nt = t; nt; nt = nt->entry){
887 		if(strcmp(nt->attr, "uid") == 0)
888 		if(strcmp(nt->val, u) == 0)
889 			break;
890 		if(strcmp(nt->attr, "gid") == 0)
891 		if(ingroup(u, nt->val))
892 			break;
893 	}
894 	ndbfree(t);
895 	return nt != nil;
896 }
897 
898 int m2p[] ={
899 	[OREAD]		4,
900 	[OWRITE]	2,
901 	[ORDWR]		6
902 };
903 
904 void
905 fsopen(Fs *fs, Request *r, Fid *f)
906 {
907 	int mode;
908 	Console *c;
909 
910 	if(f->attached == 0){
911 		fsreply(fs, r, Enofid);
912 		return;
913 	}
914 
915 	if(f->open){
916 		fsreply(fs, r, Eisopen);
917 		return;
918 	}
919 
920 	mode = r->f.mode & 3;
921 
922 	if((QTDIR & f->qid.type) && mode != OREAD){
923 		fsreply(fs, r, Eperm);
924 		return;
925 	}
926 
927 	switch(TYPE(f->qid)){
928 	case Qdata:
929 		c = fs->cons[CONS(f->qid)];
930 		if(!userok(f->user, c->name)){
931 			fsreply(fs, r, Eperm);
932 			return;
933 		}
934 		f->rp = f->buf;
935 		f->wp = f->buf;
936 		f->c = c;
937 		lock(c);
938 		f->cnext = c->flist;
939 		c->flist = f;
940 		bcastmembers(fs, c, "+", f);
941 		if(c->pid == 0)
942 			fsreopen(fs, c);
943 		unlock(c);
944 		break;
945 	case Qctl:
946 		c = fs->cons[CONS(f->qid)];
947 		if(!userok(f->user, c->name)){
948 			fsreply(fs, r, Eperm);
949 			return;
950 		}
951 		f->c = c;
952 		break;
953 	case Qstat:
954 		c = fs->cons[CONS(f->qid)];
955 		if(!userok(f->user, c->name)){
956 			fsreply(fs, r, Eperm);
957 			return;
958 		}
959 		f->c = c;
960 		break;
961 	}
962 
963 	f->open = 1;
964 	r->f.iounit = messagesize-IOHDRSZ;
965 	r->f.qid = f->qid;
966 	fsreply(fs, r, nil);
967 }
968 
969 void
970 fscreate(Fs *fs, Request *r, Fid*)
971 {
972 	fsreply(fs, r, Eperm);
973 }
974 
975 void
976 fsread(Fs *fs, Request *r, Fid *f)
977 {
978 	uchar *p, *e;
979 	int i, m, off;
980 	vlong offset;
981 	Dir d;
982 	char sbuf[ERRMAX];
983 
984 	if(f->attached == 0){
985 		fsreply(fs, r, Enofid);
986 		return;
987 	}
988 
989 	if((int)r->f.count < 0){
990 		fsreply(fs, r, Ebadcount);
991 		return;
992 	}
993 
994 	if(QTDIR & f->qid.type){
995 		p = r->buf + IOHDRSZ;
996 		e = p + r->f.count;
997 		offset = r->f.offset;
998 		off = 0;
999 		for(i=0; p<e; i++, off+=m){
1000 			m = fsdirgen(fs, f->qid, i, &d, p, e-p);
1001 			if(m < 0)
1002 				break;
1003 			if(m > BIT16SZ && off >= offset)
1004 				p += m;
1005 		}
1006 		r->f.data = (char*)r->buf + IOHDRSZ;
1007 		r->f.count = (char*)p - r->f.data;
1008 	} else {
1009 		switch(TYPE(f->qid)){
1010 		case Qdata:
1011 			addreq(&f->r, r);
1012 			fskick(fs, f);
1013 			return;
1014 		case Qctl:
1015 			r->f.data = (char*)r->buf+IOHDRSZ;
1016 			r->f.count = 0;
1017 			break;
1018 		case Qstat:
1019 			if(r->f.count > sizeof(sbuf))
1020 				r->f.count = sizeof(sbuf);
1021 			i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset);
1022 			if(i < 0){
1023 				errstr(sbuf, sizeof sbuf);
1024 				fsreply(fs, r, sbuf);
1025 				return;
1026 			}
1027 			r->f.data = sbuf;
1028 			r->f.count = i;
1029 			break;
1030 		default:
1031 			fsreply(fs, r, Eexist);
1032 			return;
1033 		}
1034 	}
1035 	fsreply(fs, r, nil);
1036 }
1037 
1038 void
1039 fswrite(Fs *fs, Request *r, Fid *f)
1040 {
1041 	int i;
1042 
1043 	if(f->attached == 0){
1044 		fsreply(fs, r, Enofid);
1045 		return;
1046 	}
1047 
1048 	if((int)r->f.count < 0){
1049 		fsreply(fs, r, Ebadcount);
1050 		return;
1051 	}
1052 
1053 	if(QTDIR & f->qid.type){
1054 		fsreply(fs, r, Eperm);
1055 		return;
1056 	}
1057 
1058 	switch(TYPE(f->qid)){
1059 	default:
1060 		fsreply(fs, r, Eperm);
1061 		return;
1062 	case Qctl:
1063 		write(f->c->cfd, r->f.data, r->f.count);
1064 		break;
1065 	case Qdata:
1066 		if(f->c->cronly)
1067 			for(i = 0; i < r->f.count; i++)
1068 				if(r->f.data[i] == '\n')
1069 					r->f.data[i] = '\r';
1070 		write(f->c->fd, r->f.data, r->f.count);
1071 		break;
1072 	}
1073 	fsreply(fs, r, nil);
1074 }
1075 
1076 void
1077 fsclunk(Fs *fs, Request *r, Fid *f)
1078 {
1079 	Fid **l, *fl;
1080 	Request *nr;
1081 
1082 	if(f->open && TYPE(f->qid) == Qdata){
1083 		while((nr = remreq(&f->r)) != nil){
1084 			fsputfid(fs, f);
1085 			free(nr);
1086 		}
1087 
1088 		lock(f->c);
1089 		for(l = &f->c->flist; *l; l = &fl->cnext){
1090 			fl = *l;
1091 			if(fl == f){
1092 				*l = fl->cnext;
1093 				break;
1094 			}
1095 		}
1096 		bcastmembers(fs, f->c, "-", f);
1097 		if(f->c->ondemand && f->c->flist == nil)
1098 			fsreopen(fs, f->c);
1099 		unlock(f->c);
1100 	}
1101 	fsreply(fs, r, nil);
1102 	fsputfid(fs, f);
1103 }
1104 
1105 void
1106 fsremove(Fs *fs, Request *r, Fid*)
1107 {
1108 	fsreply(fs, r, Eperm);
1109 }
1110 
1111 
1112 void
1113 fsstat(Fs *fs, Request *r, Fid *f)
1114 {
1115 	int i, n;
1116 	Qid q;
1117 	Dir d;
1118 
1119 	q = parentqid(f->qid);
1120 	for(i = 0; ; i++){
1121 		r->f.stat = r->buf+IOHDRSZ;
1122 		n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ);
1123 		if(n < 0){
1124 			fsreply(fs, r, Eexist);
1125 			return;
1126 		}
1127 		r->f.nstat = n;
1128 		if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path)
1129 			break;
1130 	}
1131 	fsreply(fs, r, nil);
1132 }
1133 
1134 void
1135 fswstat(Fs *fs, Request *r, Fid*)
1136 {
1137 	fsreply(fs, r, Eperm);
1138 }
1139 
1140 void
1141 fsreply(Fs *fs, Request *r, char *err)
1142 {
1143 	int n;
1144 	uchar buf[8192+IOHDRSZ];
1145 
1146 	if(err){
1147 		r->f.type = Rerror;
1148 		r->f.ename = err;
1149 	}
1150 	n = convS2M(&r->f, buf, messagesize);
1151 	if(debug)
1152 		fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n);
1153 	fsputfid(fs, r->fid);
1154 	if(write(fs->fd, buf, n) != n)
1155 		fatal("unmounted");
1156 	free(r);
1157 }
1158 
1159 /*
1160  *  called whenever input or a read request has been received
1161  */
1162 void
1163 fskick(Fs *fs, Fid *f)
1164 {
1165 	Request *r;
1166 	char *p, *rp, *wp, *ep;
1167 	int i;
1168 
1169 	lock(f);
1170 	while(f->rp != f->wp){
1171 		r = remreq(&f->r);
1172 		if(r == nil)
1173 			break;
1174 		p = (char*)r->buf;
1175 		rp = f->rp;
1176 		wp = f->wp;
1177 		ep = &f->buf[Bufsize];
1178 		for(i = 0; i < r->f.count && rp != wp; i++){
1179 			*p++ = *rp++;
1180 			if(rp >= ep)
1181 				rp = f->buf;
1182 		}
1183 		f->rp = rp;
1184 		r->f.data = (char*)r->buf;
1185 		r->f.count = p - (char*)r->buf;
1186 		fsreply(fs, r, nil);
1187 	}
1188 	unlock(f);
1189 }
1190 
1191 void
1192 usage(void)
1193 {
1194 	fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n");
1195 	threadexitsall("usage");
1196 }
1197 
1198 void
1199 threadmain(int argc, char **argv)
1200 {
1201 	fmtinstall('F', fcallfmt);
1202 
1203 	ARGBEGIN{
1204 	case 'd':
1205 		debug++;
1206 		break;
1207 	case 'c':
1208 		consoledb = ARGF();
1209 		if(consoledb == nil)
1210 			usage();
1211 		break;
1212 	case 'm':
1213 		mntpt = ARGF();
1214 		if(mntpt == nil)
1215 			usage();
1216 		break;
1217 	}ARGEND;
1218 
1219 	db = ndbopen(consoledb);
1220 	if(db == nil)
1221  		fatal("can't open %s: %r", consoledb);
1222 
1223 	fsmount(mntpt);
1224 }
1225 
1226