Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 1
/*
2
 * SSH network file system.
3
 * Presents remote TCP stack as /net-style file system.
4
 */
5
 
6
#include "ssh.h"
7
#include <bio.h>
8
#include <ndb.h>
9
#include <thread.h>
10
#include <fcall.h>
11
#include <9p.h>
12
 
13
int rawhack = 1;
14
Conn *conn;
15
char *remoteip	= "<remote>";
16
char *mtpt;
17
 
18
Cipher *allcipher[] = {
19
	&cipherrc4,
20
	&cipherblowfish,
21
	&cipher3des,
22
	&cipherdes,
23
	&ciphernone,
24
	&ciphertwiddle,
25
};
26
 
27
Auth *allauth[] = {
28
	&authpassword,
29
	&authrsa,
30
	&authtis,
31
};
32
 
33
char *cipherlist = "rc4 3des";
34
char *authlist = "rsa password tis";
35
 
36
Cipher*
37
findcipher(char *name, Cipher **list, int nlist)
38
{
39
	int i;
40
 
41
	for(i=0; i<nlist; i++)
42
		if(strcmp(name, list[i]->name) == 0)
43
			return list[i];
44
	error("unknown cipher %s", name);
45
	return nil;
46
}
47
 
48
Auth*
49
findauth(char *name, Auth **list, int nlist)
50
{
51
	int i;
52
 
53
	for(i=0; i<nlist; i++)
54
		if(strcmp(name, list[i]->name) == 0)
55
			return list[i];
56
	error("unknown auth %s", name);
57
	return nil;
58
}
59
 
60
void
61
usage(void)
62
{
63
	fprint(2, "usage: sshnet [-A authlist] [-c cipherlist] [-m mtpt] [user@]hostname\n");
64
	exits("usage");
65
}
66
 
67
int
68
isatty(int fd)
69
{
70
	char buf[64];
71
 
72
	buf[0] = '\0';
73
	fd2path(fd, buf, sizeof buf);
74
	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
75
		return 1;
76
	return 0;
77
}
78
 
79
enum
80
{
81
	Qroot,
82
	Qcs,
83
	Qtcp,
84
	Qclone,
85
	Qn,
86
	Qctl,
87
	Qdata,
88
	Qlocal,
89
	Qremote,
90
	Qstatus,
91
};
92
 
93
#define PATH(type, n)		((type)|((n)<<8))
94
#define TYPE(path)			((int)(path) & 0xFF)
95
#define NUM(path)			((uint)(path)>>8)
96
 
97
Channel *sshmsgchan;		/* chan(Msg*) */
98
Channel *fsreqchan;			/* chan(Req*) */
99
Channel *fsreqwaitchan;		/* chan(nil) */
100
Channel *fsclunkchan;		/* chan(Fid*) */
101
Channel *fsclunkwaitchan;	/* chan(nil) */
102
ulong time0;
103
 
104
enum
105
{
106
	Closed,
107
	Dialing,
108
	Established,
109
	Teardown,
110
};
111
 
112
char *statestr[] = {
113
	"Closed",
114
	"Dialing",
115
	"Established",
116
	"Teardown",
117
};
118
 
119
typedef struct Client Client;
120
struct Client
121
{
122
	int ref;
123
	int state;
124
	int num;
125
	int servernum;
126
	char *connect;
127
	Req *rq;
128
	Req **erq;
129
	Msg *mq;
130
	Msg **emq;
131
};
132
 
133
int nclient;
134
Client **client;
135
 
136
int
137
newclient(void)
138
{
139
	int i;
140
	Client *c;
141
 
142
	for(i=0; i<nclient; i++)
143
		if(client[i]->ref==0 && client[i]->state == Closed)
144
			return i;
145
 
146
	if(nclient%16 == 0)
147
		client = erealloc9p(client, (nclient+16)*sizeof(client[0]));
148
 
149
	c = emalloc9p(sizeof(Client));
150
	memset(c, 0, sizeof(*c));
151
	c->num = nclient;
152
	client[nclient++] = c;
153
	return c->num;
154
}
155
 
156
void
157
queuereq(Client *c, Req *r)
158
{
159
	if(c->rq==nil)
160
		c->erq = &c->rq;
161
	*c->erq = r;
162
	r->aux = nil;
163
	c->erq = (Req**)&r->aux;
164
}
165
 
166
void
167
queuemsg(Client *c, Msg *m)
168
{
169
	if(c->mq==nil)
170
		c->emq = &c->mq;
171
	*c->emq = m;
172
	m->link = nil;
173
	c->emq = (Msg**)&m->link;
174
}
175
 
176
void
177
matchmsgs(Client *c)
178
{
179
	Req *r;
180
	Msg *m;
181
	int n, rm;
182
 
183
	while(c->rq && c->mq){
184
		r = c->rq;
185
		c->rq = r->aux;
186
 
187
		rm = 0;
188
		m = c->mq;
189
		n = r->ifcall.count;
190
		if(n >= m->ep - m->rp){
191
			n = m->ep - m->rp;
192
			c->mq = m->link;
193
			rm = 1;
194
		}
195
		memmove(r->ofcall.data, m->rp, n);
196
		if(rm)
197
			free(m);
198
		else
199
			m->rp += n;
200
		r->ofcall.count = n;
201
		respond(r, nil);
202
	}
203
}
204
 
205
Req*
206
findreq(Client *c, Req *r)
207
{
208
	Req **l;
209
 
210
	for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
211
		if(*l == r){
212
			*l = r->aux;
213
			if(*l == nil)
214
				c->erq = l;
215
			return r;
216
		}
217
	}
218
	return nil;
219
}
220
 
221
void
222
dialedclient(Client *c)
223
{
224
	Req *r;
225
 
226
	if(r=c->rq){
227
		if(r->aux != nil)
228
			sysfatal("more than one outstanding dial request (BUG)");
229
		if(c->state == Established)
230
			respond(r, nil);
231
		else
232
			respond(r, "connect failed");
233
	}
234
	c->rq = nil;
235
}
236
 
237
void
238
teardownclient(Client *c)
239
{
240
	Msg *m;
241
 
242
	c->state = Teardown;
243
	m = allocmsg(conn, SSH_MSG_CHANNEL_INPUT_EOF, 4);
244
	putlong(m, c->servernum);
245
	sendmsg(m);
246
}
247
 
248
void
249
hangupclient(Client *c)
250
{
251
	Req *r, *next;
252
	Msg *m, *mnext;
253
 
254
	c->state = Closed;
255
	for(m=c->mq; m; m=mnext){
256
		mnext = m->link;
257
		free(m);
258
	}
259
	c->mq = nil;
260
	for(r=c->rq; r; r=next){
261
		next = r->aux;
262
		respond(r, "hangup on network connection");
263
	}
264
	c->rq = nil;
265
}
266
 
267
void
268
closeclient(Client *c)
269
{
270
	Msg *m, *next;
271
 
272
	if(--c->ref)
273
		return;
274
 
275
	if(c->rq != nil)
276
		sysfatal("ref count reached zero with requests pending (BUG)");
277
 
278
	for(m=c->mq; m; m=next){
279
		next = m->link;
280
		free(m);
281
	}
282
	c->mq = nil;
283
 
284
	if(c->state != Closed)
285
		teardownclient(c);
286
}
287
 
288
 
289
void
290
sshreadproc(void *a)
291
{
292
	Conn *c;
293
	Msg *m;
294
 
295
	c = a;
296
	for(;;){
297
		m = recvmsg(c, -1);
298
		if(m == nil)
299
			sysfatal("eof on ssh connection");
300
		sendp(sshmsgchan, m);
301
	}
302
}
303
 
304
typedef struct Tab Tab;
305
struct Tab
306
{
307
	char *name;
308
	ulong mode;
309
};
310
 
311
Tab tab[] =
312
{
313
	"/",		DMDIR|0555,
314
	"cs",		0666,
315
	"tcp",	DMDIR|0555,	
316
	"clone",	0666,
317
	nil,		DMDIR|0555,
318
	"ctl",		0666,
319
	"data",	0666,
320
	"local",	0444,
321
	"remote",	0444,
322
	"status",	0444,
323
};
324
 
325
static void
326
fillstat(Dir *d, uvlong path)
327
{
328
	Tab *t;
329
 
330
	memset(d, 0, sizeof(*d));
331
	d->uid = estrdup9p("ssh");
332
	d->gid = estrdup9p("ssh");
333
	d->qid.path = path;
334
	d->atime = d->mtime = time0;
335
	t = &tab[TYPE(path)];
336
	if(t->name)
337
		d->name = estrdup9p(t->name);
338
	else{
339
		d->name = smprint("%ud", NUM(path));
340
		if(d->name == nil)
341
			sysfatal("out of memory");
342
	}
343
	d->qid.type = t->mode>>24;
344
	d->mode = t->mode;
345
}
346
 
347
static void
348
fsattach(Req *r)
349
{
350
	if(r->ifcall.aname && r->ifcall.aname[0]){
351
		respond(r, "invalid attach specifier");
352
		return;
353
	}
354
	r->fid->qid.path = PATH(Qroot, 0);
355
	r->fid->qid.type = QTDIR;
356
	r->fid->qid.vers = 0;
357
	r->ofcall.qid = r->fid->qid;
358
	respond(r, nil);
359
}
360
 
361
static void
362
fsstat(Req *r)
363
{
364
	fillstat(&r->d, r->fid->qid.path);
365
	respond(r, nil);
366
}
367
 
368
static int
369
rootgen(int i, Dir *d, void*)
370
{
371
	i += Qroot+1;
372
	if(i <= Qtcp){
373
		fillstat(d, i);
374
		return 0;
375
	}
376
	return -1;
377
}
378
 
379
static int
380
tcpgen(int i, Dir *d, void*)
381
{
382
	i += Qtcp+1;
383
	if(i < Qn){
384
		fillstat(d, i);
385
		return 0;
386
	}
387
	i -= Qn;
388
	if(i < nclient){
389
		fillstat(d, PATH(Qn, i));
390
		return 0;
391
	}
392
	return -1;
393
}
394
 
395
static int
396
clientgen(int i, Dir *d, void *aux)
397
{
398
	Client *c;
399
 
400
	c = aux;
401
	i += Qn+1;
402
	if(i <= Qstatus){
403
		fillstat(d, PATH(i, c->num));
404
		return 0;
405
	}
406
	return -1;
407
}
408
 
409
static char*
410
fswalk1(Fid *fid, char *name, Qid *qid)
411
{
412
	int i, n;
413
	char buf[32];
414
	ulong path;
415
 
416
	path = fid->qid.path;
417
	if(!(fid->qid.type&QTDIR))
418
		return "walk in non-directory";
419
 
420
	if(strcmp(name, "..") == 0){
421
		switch(TYPE(path)){
422
		case Qn:
423
			qid->path = PATH(Qtcp, NUM(path));
424
			qid->type = tab[Qtcp].mode>>24;
425
			return nil;
426
		case Qtcp:
427
			qid->path = PATH(Qroot, 0);
428
			qid->type = tab[Qroot].mode>>24;
429
			return nil;
430
		case Qroot:
431
			return nil;
432
		default:
433
			return "bug in fswalk1";
434
		}
435
	}
436
 
437
	i = TYPE(path)+1;
438
	for(; i<nelem(tab); i++){
439
		if(i==Qn){
440
			n = atoi(name);
441
			snprint(buf, sizeof buf, "%d", n);
442
			if(n < nclient && strcmp(buf, name) == 0){
443
				qid->path = PATH(i, n);
444
				qid->type = tab[i].mode>>24;
445
				return nil;
446
			}
447
			break;
448
		}
449
		if(strcmp(name, tab[i].name) == 0){
450
			qid->path = PATH(i, NUM(path));
451
			qid->type = tab[i].mode>>24;
452
			return nil;
453
		}
454
		if(tab[i].mode&DMDIR)
455
			break;
456
	}
457
	return "directory entry not found";
458
}
459
 
460
typedef struct Cs Cs;
461
struct Cs
462
{
463
	char *resp;
464
	int isnew;
465
};
466
 
467
static int
468
ndbfindport(char *p)
469
{
470
	char *s, *port;
471
	int n;
472
	static Ndb *db;
473
 
474
	if(*p == '\0')
475
		return -1;
476
 
477
	n = strtol(p, &s, 0);
478
	if(*s == '\0')
479
		return n;
480
 
481
	if(db == nil){
482
		db = ndbopen("/lib/ndb/common");
483
		if(db == nil)
484
			return -1;
485
	}
486
 
487
	port = ndbgetvalue(db, nil, "tcp", p, "port", nil);
488
	if(port == nil)
489
		return -1;
490
	n = atoi(port);
491
	free(port);
492
 
493
	return n;
494
}	
495
 
496
static void
497
csread(Req *r)
498
{
499
	Cs *cs;
500
 
501
	cs = r->fid->aux;
502
	if(cs->resp==nil){
503
		respond(r, "cs read without write");
504
		return;
505
	}
506
	if(r->ifcall.offset==0){
507
		if(!cs->isnew){
508
			r->ofcall.count = 0;
509
			respond(r, nil);
510
			return;
511
		}
512
		cs->isnew = 0;
513
	}
514
	readstr(r, cs->resp);
515
	respond(r, nil);
516
}
517
 
518
static void
519
cswrite(Req *r)
520
{
521
	int port, nf;
522
	char err[ERRMAX], *f[4], *s, *ns;
523
	Cs *cs;
524
 
525
	cs = r->fid->aux;
526
	s = emalloc(r->ifcall.count+1);
527
	memmove(s, r->ifcall.data, r->ifcall.count);
528
	s[r->ifcall.count] = '\0';
529
 
530
	nf = getfields(s, f, nelem(f), 0, "!");
531
	if(nf != 3){
532
		free(s);
533
		respond(r, "can't translate");
534
		return;
535
	}
536
	if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){
537
		free(s);
538
		respond(r, "unknown protocol");
539
		return;
540
	}
541
	port = ndbfindport(f[2]);
542
	if(port <= 0){
543
		free(s);
544
		respond(r, "no translation found");
545
		return;
546
	}
547
 
548
	ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port);
549
	if(ns == nil){
550
		free(s);
551
		rerrstr(err, sizeof err);
552
		respond(r, err);
553
		return;
554
	}
555
	free(s);
556
	free(cs->resp);
557
	cs->resp = ns;
558
	cs->isnew = 1;
559
	r->ofcall.count = r->ifcall.count;
560
	respond(r, nil);
561
}
562
 
563
static void
564
ctlread(Req *r, Client *c)
565
{
566
	char buf[32];
567
 
568
	sprint(buf, "%d", c->num);
569
	readstr(r, buf);
570
	respond(r, nil);
571
}
572
 
573
static void
574
ctlwrite(Req *r, Client *c)
575
{
576
	char *f[3], *s;
577
	int nf;
578
	Msg *m;
579
 
580
	s = emalloc(r->ifcall.count+1);
581
	memmove(s, r->ifcall.data, r->ifcall.count);
582
	s[r->ifcall.count] = '\0';
583
 
584
	nf = tokenize(s, f, 3);
585
	if(nf == 0){
586
		free(s);
587
		respond(r, nil);
588
		return;
589
	}
590
 
591
	if(strcmp(f[0], "hangup") == 0){
592
		if(c->state != Established)
593
			goto Badarg;
594
		if(nf != 1)
595
			goto Badarg;
596
		queuereq(c, r);
597
		teardownclient(c);
598
	}else if(strcmp(f[0], "connect") == 0){
599
		if(c->state != Closed)
600
			goto Badarg;
601
		if(nf != 2)
602
			goto Badarg;
603
		c->connect = estrdup9p(f[1]);
604
		nf = getfields(f[1], f, nelem(f), 0, "!");
605
		if(nf != 2){
606
			free(c->connect);
607
			c->connect = nil;
608
			goto Badarg;
609
		}
610
		c->state = Dialing;
611
		m = allocmsg(conn, SSH_MSG_PORT_OPEN, 4+4+strlen(f[0])+4+4+strlen("localhost"));
612
		putlong(m, c->num);
613
		putstring(m, f[0]);
614
		putlong(m, ndbfindport(f[1]));
615
		putstring(m, "localhost");
616
		queuereq(c, r);
617
		sendmsg(m);
618
	}else{
619
	Badarg:
620
		respond(r, "bad or inappropriate tcp control message");
621
	}
622
	free(s);
623
}
624
 
625
static void
626
dataread(Req *r, Client *c)
627
{
628
	if(c->state != Established){
629
		respond(r, "not connected");
630
		return;
631
	}
632
	queuereq(c, r);
633
	matchmsgs(c);
634
}
635
 
636
static void
637
datawrite(Req *r, Client *c)
638
{
639
	Msg *m;
640
 
641
	if(c->state != Established){
642
		respond(r, "not connected");
643
		return;
644
	}
645
	if(r->ifcall.count){
646
		m = allocmsg(conn, SSH_MSG_CHANNEL_DATA, 4+4+r->ifcall.count);
647
		putlong(m, c->servernum);
648
		putlong(m, r->ifcall.count);
649
		putbytes(m, r->ifcall.data, r->ifcall.count);
650
		sendmsg(m);
651
	}
652
	r->ofcall.count = r->ifcall.count;
653
	respond(r, nil);
654
}
655
 
656
static void
657
localread(Req *r)
658
{
659
	char buf[128];
660
 
661
	snprint(buf, sizeof buf, "%s!%d\n", remoteip, 0);
662
	readstr(r, buf);
663
	respond(r, nil);
664
}
665
 
666
static void
667
remoteread(Req *r, Client *c)
668
{
669
	char *s;
670
	char buf[128];
671
 
672
	s = c->connect;
673
	if(s == nil)
674
		s = "::!0";
675
	snprint(buf, sizeof buf, "%s\n", s);
676
	readstr(r, buf);
677
	respond(r, nil);
678
}
679
 
680
static void
681
statusread(Req *r, Client *c)
682
{
683
	char buf[64];
684
	char *s;
685
 
686
	snprint(buf, sizeof buf, "%s!%d", remoteip, 0);
687
	s = statestr[c->state];
688
	readstr(r, s);
689
	respond(r, nil);
690
}
691
 
692
static void
693
fsread(Req *r)
694
{
695
	char e[ERRMAX];
696
	ulong path;
697
 
698
	path = r->fid->qid.path;
699
	switch(TYPE(path)){
700
	default:
701
		snprint(e, sizeof e, "bug in fsread path=%lux", path);
702
		respond(r, e);
703
		break;
704
 
705
	case Qroot:
706
		dirread9p(r, rootgen, nil);
707
		respond(r, nil);
708
		break;
709
 
710
	case Qcs:
711
		csread(r);
712
		break;
713
 
714
	case Qtcp:
715
		dirread9p(r, tcpgen, nil);
716
		respond(r, nil);
717
		break;
718
 
719
	case Qn:
720
		dirread9p(r, clientgen, client[NUM(path)]);
721
		respond(r, nil);
722
		break;
723
 
724
	case Qctl:
725
		ctlread(r, client[NUM(path)]);
726
		break;
727
 
728
	case Qdata:
729
		dataread(r, client[NUM(path)]);
730
		break;
731
 
732
	case Qlocal:
733
		localread(r);
734
		break;
735
 
736
	case Qremote:
737
		remoteread(r, client[NUM(path)]);
738
		break;
739
 
740
	case Qstatus:
741
		statusread(r, client[NUM(path)]);
742
		break;
743
	}
744
}
745
 
746
static void
747
fswrite(Req *r)
748
{
749
	ulong path;
750
	char e[ERRMAX];
751
 
752
	path = r->fid->qid.path;
753
	switch(TYPE(path)){
754
	default:
755
		snprint(e, sizeof e, "bug in fswrite path=%lux", path);
756
		respond(r, e);
757
		break;
758
 
759
	case Qcs:
760
		cswrite(r);
761
		break;
762
 
763
	case Qctl:
764
		ctlwrite(r, client[NUM(path)]);
765
		break;
766
 
767
	case Qdata:
768
		datawrite(r, client[NUM(path)]);
769
		break;
770
	}
771
}
772
 
773
static void
774
fsopen(Req *r)
775
{
776
	static int need[4] = { 4, 2, 6, 1 };
777
	ulong path;
778
	int n;
779
	Tab *t;
780
	Cs *cs;
781
 
782
	/*
783
	 * lib9p already handles the blatantly obvious.
784
	 * we just have to enforce the permissions we have set.
785
	 */
786
	path = r->fid->qid.path;
787
	t = &tab[TYPE(path)];
788
	n = need[r->ifcall.mode&3];
789
	if((n&t->mode) != n){
790
		respond(r, "permission denied");
791
		return;
792
	}
793
 
794
	switch(TYPE(path)){
795
	case Qcs:
796
		cs = emalloc(sizeof(Cs));
797
		r->fid->aux = cs;
798
		respond(r, nil);
799
		break;
800
	case Qclone:
801
		n = newclient();
802
		path = PATH(Qctl, n);
803
		r->fid->qid.path = path;
804
		r->ofcall.qid.path = path;
805
		if(chatty9p)
806
			fprint(2, "open clone => path=%lux\n", path);
807
		t = &tab[Qctl];
808
		/* fall through */
809
	default:
810
		if(t-tab >= Qn)
811
			client[NUM(path)]->ref++;
812
		respond(r, nil);
813
		break;
814
	}
815
}
816
 
817
static void
818
fsflush(Req *r)
819
{
820
	int i;
821
 
822
	for(i=0; i<nclient; i++)
823
		if(findreq(client[i], r->oldreq))
824
			respond(r->oldreq, "interrupted");
825
	respond(r, nil);
826
}
827
 
828
static void
829
handlemsg(Msg *m)
830
{
831
	int chan, n;
832
	Client *c;
833
 
834
	switch(m->type){
835
	case SSH_MSG_DISCONNECT:
836
	case SSH_CMSG_EXIT_CONFIRMATION:
837
		sysfatal("disconnect");
838
 
839
	case SSH_CMSG_STDIN_DATA:
840
	case SSH_CMSG_EOF:
841
	case SSH_CMSG_WINDOW_SIZE:
842
		/* don't care */
843
		free(m);
844
		break;
845
 
846
	case SSH_MSG_CHANNEL_DATA:
847
		chan = getlong(m);
848
		n = getlong(m);
849
		if(m->rp+n != m->ep)
850
			sysfatal("got bad channel data");
851
		if(chan<nclient && (c=client[chan])->state==Established){
852
			queuemsg(c, m);
853
			matchmsgs(c);
854
		}else
855
			free(m);
856
		break;
857
 
858
	case SSH_MSG_CHANNEL_INPUT_EOF:
859
		chan = getlong(m);
860
		free(m);
861
		if(chan<nclient){
862
			c = client[chan];
863
			chan = c->servernum;
864
			hangupclient(c);
865
			m = allocmsg(conn, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4);
866
			putlong(m, chan);
867
			sendmsg(m);
868
		}
869
		break;
870
 
871
	case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
872
		chan = getlong(m);
873
		if(chan<nclient)
874
			hangupclient(client[chan]);
875
		free(m);
876
		break;
877
 
878
	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
879
		chan = getlong(m);
880
		c = nil;
881
		if(chan>=nclient || (c=client[chan])->state != Dialing){
882
			if(c)
883
				fprint(2, "cstate %d\n", c->state);
884
			sysfatal("got unexpected open confirmation for %d", chan);
885
		}
886
		c->servernum = getlong(m);
887
		c->state = Established;
888
		dialedclient(c);
889
		free(m);
890
		break;
891
 
892
	case SSH_MSG_CHANNEL_OPEN_FAILURE:
893
		chan = getlong(m);
894
		c = nil;
895
		if(chan>=nclient || (c=client[chan])->state != Dialing)
896
			sysfatal("got unexpected open failure");
897
		if(m->rp+4 <= m->ep)
898
			c->servernum = getlong(m);
899
		c->state = Closed;
900
		dialedclient(c);
901
		free(m);
902
		break;
903
	}
904
}
905
 
906
void
907
fsnetproc(void*)
908
{
909
	ulong path;
910
	Alt a[4];
911
	Cs *cs;
912
	Fid *fid;
913
	Req *r;
914
	Msg *m;
915
 
916
	threadsetname("fsthread");
917
 
918
	a[0].op = CHANRCV;
919
	a[0].c = fsclunkchan;
920
	a[0].v = &fid;
921
	a[1].op = CHANRCV;
922
	a[1].c = fsreqchan;
923
	a[1].v = &r;
924
	a[2].op = CHANRCV;
925
	a[2].c = sshmsgchan;
926
	a[2].v = &m;
927
	a[3].op = CHANEND;
928
 
929
	for(;;){
930
		switch(alt(a)){
931
		case 0:
932
			path = fid->qid.path;
933
			switch(TYPE(path)){
934
			case Qcs:
935
				cs = fid->aux;
936
				if(cs){
937
					free(cs->resp);
938
					free(cs);
939
				}
940
				break;
941
			}
942
			if(fid->omode != -1 && TYPE(path) >= Qn)
943
				closeclient(client[NUM(path)]);
944
			sendp(fsclunkwaitchan, nil);
945
			break;
946
		case 1:
947
			switch(r->ifcall.type){
948
			case Tattach:
949
				fsattach(r);
950
				break;
951
			case Topen:
952
				fsopen(r);
953
				break;
954
			case Tread:
955
				fsread(r);
956
				break;
957
			case Twrite:
958
				fswrite(r);
959
				break;
960
			case Tstat:
961
				fsstat(r);
962
				break;
963
			case Tflush:
964
				fsflush(r);
965
				break;
966
			default:
967
				respond(r, "bug in fsthread");
968
				break;
969
			}
970
			sendp(fsreqwaitchan, 0);
971
			break;
972
		case 2:
973
			handlemsg(m);
974
			break;
975
		}
976
	}
977
}
978
 
979
static void
980
fssend(Req *r)
981
{
982
	sendp(fsreqchan, r);
983
	recvp(fsreqwaitchan);	/* avoids need to deal with spurious flushes */
984
}
985
 
986
static void
987
fsdestroyfid(Fid *fid)
988
{
989
	sendp(fsclunkchan, fid);
990
	recvp(fsclunkwaitchan);
991
}
992
 
993
void
994
takedown(Srv*)
995
{
996
	threadexitsall("done");
997
}
998
 
999
Srv fs = 
1000
{
1001
.attach=		fssend,
1002
.destroyfid=	fsdestroyfid,
1003
.walk1=		fswalk1,
1004
.open=		fssend,
1005
.read=		fssend,
1006
.write=		fssend,
1007
.stat=		fssend,
1008
.flush=		fssend,
1009
.end=		takedown,
1010
};
1011
 
1012
void
1013
threadmain(int argc, char **argv)
1014
{
1015
	int i, fd;
1016
	char *host, *user, *p, *service;
1017
	char *f[16];
1018
	Msg *m;
1019
	static Conn c;
1020
 
1021
	fmtinstall('B', mpfmt);
1022
	fmtinstall('H', encodefmt);
1023
 
1024
	mtpt = "/net";
1025
	service = nil;
1026
	user = nil;
1027
	ARGBEGIN{
1028
	case 'B':	/* undocumented, debugging */
1029
		doabort = 1;
1030
		break;
1031
	case 'D':	/* undocumented, debugging */
1032
		debuglevel = strtol(EARGF(usage()), nil, 0);
1033
		break;
1034
	case '9':	/* undocumented, debugging */
1035
		chatty9p++;
1036
		break;
1037
 
1038
	case 'A':
1039
		authlist = EARGF(usage());
1040
		break;
1041
	case 'c':
1042
		cipherlist = EARGF(usage());
1043
		break;
1044
	case 'm':
1045
		mtpt = EARGF(usage());
1046
		break;
1047
	case 's':
1048
		service = EARGF(usage());
1049
		break;
1050
	default:
1051
		usage();
1052
	}ARGEND
1053
 
1054
	if(argc != 1)
1055
		usage();
1056
 
1057
	host = argv[0];
1058
 
1059
	if((p = strchr(host, '@')) != nil){
1060
		*p++ = '\0';
1061
		user = host;
1062
		host = p;
1063
	}
1064
	if(user == nil)
1065
		user = getenv("user");
1066
	if(user == nil)
1067
		sysfatal("cannot find user name");
1068
 
1069
	privatefactotum();
1070
 
1071
	if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
1072
		sysfatal("dialing %s: %r", host);
1073
 
1074
	c.interactive = isatty(0);
1075
	c.fd[0] = c.fd[1] = fd;
1076
	c.user = user;
1077
	c.host = host;
1078
	setaliases(&c, host);
1079
 
1080
	c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
1081
	c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
1082
	for(i=0; i<c.nokcipher; i++)
1083
		c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
1084
 
1085
	c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
1086
	c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
1087
	for(i=0; i<c.nokauth; i++)
1088
		c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
1089
 
1090
	sshclienthandshake(&c);
1091
 
1092
	requestpty(&c);		/* turns on TCP_NODELAY on other side */
1093
	m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
1094
	sendmsg(m);
1095
 
1096
	time0 = time(0);
1097
	sshmsgchan = chancreate(sizeof(Msg*), 16);
1098
	fsreqchan = chancreate(sizeof(Req*), 0);
1099
	fsreqwaitchan = chancreate(sizeof(void*), 0);
1100
	fsclunkchan = chancreate(sizeof(Fid*), 0);
1101
	fsclunkwaitchan = chancreate(sizeof(void*), 0);
1102
 
1103
	conn = &c;
1104
	procrfork(sshreadproc, &c, 8192, RFNAMEG|RFNOTEG);
1105
	procrfork(fsnetproc, nil, 8192, RFNAMEG|RFNOTEG);
1106
 
1107
	threadpostmountsrv(&fs, service, mtpt, MREPL);
1108
	exits(0);
1109
}
1110