xref: /plan9/sys/src/cmd/aux/consolefs.c (revision 8498559bac70ac8118e6f36ad9d674181f43f45d)
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