Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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*
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*
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
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*
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*
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
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
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*
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
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
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
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
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
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
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
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
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
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*
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
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
753
fsauth(Fs *fs, Request *r, Fid*)
754
{
755
	fsreply(fs, r, "consolefs: authentication not required");
756
}
757
 
758
void
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
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
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
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
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
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
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
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
1011
fscreate(Fs *fs, Request *r, Fid*)
1012
{
1013
	fsreply(fs, r, Eperm);
1014
}
1015
 
1016
void
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
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
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
1172
fsremove(Fs *fs, Request *r, Fid*)
1173
{
1174
	fsreply(fs, r, Eperm);
1175
}
1176
 
1177
void
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
1200
fswstat(Fs *fs, Request *r, Fid*)
1201
{
1202
	fsreply(fs, r, Eperm);
1203
}
1204
 
1205
void
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
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
1257
usage(void)
1258
{
1259
	fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n");
1260
	threadexitsall("usage");
1261
}
1262
 
1263
void
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
}