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