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
 
4
int debug;		/* true if debugging */
5
int ctl = -1;		/* control fd (for break's) */
6
int raw;		/* true if raw is on */
7
int consctl = -1;	/* control fd for cons */
8
int ttypid;		/* pid's if the 2 processes (used to kill them) */
9
int outfd = 1;		/* local output file descriptor */
10
int cooked;		/* non-zero forces cooked mode */
11
int returns;		/* non-zero forces carriage returns not to be filtered out */
12
int crtonl;			/* non-zero forces carriage returns to be converted to nls coming from net */
13
int	strip;		/* strip off parity bits */
14
char firsterr[2*ERRMAX];
15
char transerr[2*ERRMAX];
16
int limited;
17
char *remuser;		/* for BSD rlogin authentication */
18
int verbose;
19
int baud;
20
int notkbd;
21
int nltocr;		/* translate kbd nl to cr  and vice versa */
22
 
23
static char *srv;
24
 
25
#define MAXMSG (2*8192)
26
 
27
int	dodial(char*, char*, char*);
28
void	fromkbd(int);
29
void	fromnet(int);
30
long	iread(int, void*, int);
31
long	iwrite(int, void*, int);
32
int	menu(int);
33
void	notifyf(void*, char*);
34
void	pass(int, int, int);
35
void	rawoff(void);
36
void	rawon(void);
37
void	stdcon(int);
38
char*	system(int, char*);
39
void	dosystem(int, char*);
40
int	wasintr(void);
41
void	punt(char*);
42
char*	syserr(void);
43
void	seterr(char*);
44
 
45
/* protocols */
46
void	device(char*, char*);
47
void	rlogin(char*, char*);
48
void	simple(char*, char*);
49
 
50
void
51
usage(void)
52
{
53
	punt("usage: con [-CdnrRsTv] [-b baud] [-l [user]] [-c cmd] [-S svc] "
54
		"net!host[!service]");
55
}
56
 
57
void
58
main(int argc, char *argv[])
59
{
60
	char *dest;
61
	char *cmd = 0;
62
 
63
	returns = 1;
64
	ARGBEGIN{
65
	case 'b':
66
		baud = atoi(EARGF(usage()));
67
		break;
68
	case 'C':
69
		cooked = 1;
70
		break;
71
	case 'c':
72
		cmd = EARGF(usage());
73
		break;
74
	case 'd':
75
		debug = 1;
76
		break;
77
	case 'l':
78
		limited = 1;
79
		if(argv[1] != nil && argv[1][0] != '-')
80
			remuser = EARGF(usage());
81
		break;
82
	case 'n':
83
		notkbd = 1;
84
		break;
85
	case 'r':
86
		returns = 0;
87
		break;
88
	case 's':
89
		strip = 1;
90
		break;
91
	case 'S':
92
		srv = EARGF(usage());
93
		break;
94
	case 'R':
95
		nltocr = 1;
96
		break;
97
	case 'T':
98
		crtonl = 1;
99
		break;
100
	case 'v':
101
		verbose = 1;
102
		break;
103
	default:
104
		usage();
105
	}ARGEND
106
 
107
	if(argc != 1){
108
		if(remuser == 0)
109
			usage();
110
		dest = remuser;
111
		remuser = 0;
112
	} else
113
		dest = argv[0];
114
	if(*dest == '/' && strchr(dest, '!') == 0)
115
		device(dest, cmd);
116
	else if(limited){
117
		simple(dest, cmd);	/* doesn't return if dialout succeeds */
118
		rlogin(dest, cmd);	/* doesn't return if dialout succeeds */
119
	} else {
120
		rlogin(dest, cmd);	/* doesn't return if dialout succeeds */
121
		simple(dest, cmd);	/* doesn't return if dialout succeeds */
122
	}
123
	punt(firsterr);
124
}
125
 
126
/*
127
 *  just dial and use as a byte stream with remote echo
128
 */
129
void
130
simple(char *dest, char *cmd)
131
{
132
	int net;
133
 
134
	net = dodial(dest, 0, 0);
135
	if(net < 0)
136
		return;
137
 
138
	if(cmd)
139
		dosystem(net, cmd);
140
 
141
	if(!cooked)
142
		rawon();
143
	stdcon(net);
144
	exits(0);
145
}
146
 
147
/*
148
 *  dial, do UCB authentication, use as a byte stream with local echo
149
 *
150
 *  return if dial failed
151
 */
152
void
153
rlogin(char *dest, char *cmd)
154
{
155
	int net;
156
	char buf[128];
157
	char *p;
158
	char *localuser;
159
 
160
	/* only useful on TCP */
161
	if(strchr(dest, '!')
162
	&& (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0))
163
		return;
164
 
165
	net = dodial(dest, "tcp", "login");
166
	if(net < 0)
167
		return;
168
 
169
	/*
170
	 *  do UCB rlogin authentication
171
	 */
172
	localuser = getuser();
173
	if(remuser == 0){
174
		if(limited)
175
			remuser = ":";
176
		else
177
			remuser = localuser;
178
	}
179
	p = getenv("TERM");
180
	if(p == 0)
181
		p = "p9";
182
	if(write(net, "", 1)<0
183
	|| write(net, localuser, strlen(localuser)+1)<0
184
	|| write(net, remuser, strlen(remuser)+1)<0
185
	|| write(net, p, strlen(p)+1)<0){
186
		close(net);
187
		punt("BSD authentication failed");
188
	}
189
	if(read(net, buf, 1) != 1)
190
		punt("BSD authentication failed1");
191
	if(buf[0] != 0){
192
		fprint(2, "con: remote error: ");
193
		while(read(net, buf, 1) == 1){
194
			write(2, buf, 1);
195
			if(buf[0] == '\n')
196
				break;
197
		}
198
		exits("read");
199
	}
200
 
201
	if(cmd)
202
		dosystem(net, cmd);
203
 
204
	if(!cooked)
205
		rawon();
206
	nltocr = 1;
207
	stdcon(net);
208
	exits(0);
209
}
210
 
211
/*
212
 *  just open a device and use it as a connection
213
 */
214
void
215
device(char *dest, char *cmd)
216
{
217
	int net;
218
	char cname[128];
219
 
220
	net = open(dest, ORDWR);
221
	if(net < 0) {
222
		fprint(2, "con: cannot open %s: %r\n", dest);
223
		exits("open");
224
	}
225
	snprint(cname, sizeof cname, "%sctl", dest);
226
	ctl = open(cname, ORDWR);
227
	if (baud > 0) {
228
		if(ctl >= 0){
229
			/* set speed and use fifos if available */
230
			fprint(ctl, "b%d i1", baud);
231
		}
232
		else
233
			fprint(2, "con: cannot open %s: %r\n", cname);
234
	}
235
 
236
	if(cmd)
237
		dosystem(net, cmd);
238
 
239
	if(!cooked)
240
		rawon();
241
	stdcon(net);
242
	exits(0);
243
}
244
 
245
/*
246
 *  ignore interrupts
247
 */
248
void
249
notifyf(void *a, char *msg)
250
{
251
	USED(a);
252
 
253
	if(strstr(msg, "yankee"))
254
		noted(NDFLT);
255
	if(strstr(msg, "closed pipe")
256
	|| strcmp(msg, "interrupt") == 0
257
	|| strcmp(msg, "hangup") == 0)
258
		noted(NCONT);
259
	noted(NDFLT);
260
}
261
 
262
/*
263
 *  turn keyboard raw mode on
264
 */
265
void
266
rawon(void)
267
{
268
	if(debug)
269
		fprint(2, "rawon\n");
270
	if(raw)
271
		return;
272
	if(consctl < 0)
273
		consctl = open("/dev/consctl", OWRITE);
274
	if(consctl < 0){
275
//		fprint(2, "can't open consctl\n");
276
		return;
277
	}
278
	write(consctl, "rawon", 5);
279
	raw = 1;
280
}
281
 
282
/*
283
 *  turn keyboard raw mode off
284
 */
285
void
286
rawoff(void)
287
{
288
	if(debug)
289
		fprint(2, "rawoff\n");
290
	if(raw == 0)
291
		return;
292
	if(consctl < 0)
293
		consctl = open("/dev/consctl", OWRITE);
294
	if(consctl < 0){
295
//		fprint(2, "can't open consctl\n");
296
		return;
297
	}
298
	write(consctl, "rawoff", 6);
299
	raw = 0;
300
}
301
 
302
/*
303
 *  control menu
304
 */
305
#define STDHELP	"\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
306
 
307
int
308
menu(int net)
309
{
310
	char buf[MAXMSG];
311
	long n;
312
	int done;
313
	int wasraw = raw;
314
 
315
	if(wasraw)
316
		rawoff();
317
 
318
	fprint(2, ">>> ");
319
	for(done = 0; !done; ){
320
		n = read(0, buf, sizeof(buf)-1);
321
		if(n <= 0)
322
			return -1;
323
		buf[n] = 0;
324
		switch(buf[0]){
325
		case '!':
326
			print(buf);
327
			system(net, buf+1);
328
			print("!\n");
329
			done = 1;
330
			break;
331
		case '.':
332
			done = 1;
333
			break;
334
		case 'q':
335
			return -1;
336
		case 'i':
337
			buf[0] = 0x1c;
338
			write(net, buf, 1);
339
			done = 1;
340
			break;
341
		case 'b':
342
			if(ctl >= 0)
343
				write(ctl, "k", 1);
344
			done = 1;
345
			break;
346
		case 'r':
347
			returns = 1-returns;
348
			done = 1;
349
			break;
350
		default:
351
			fprint(2, STDHELP);
352
			break;
353
		}
354
		if(!done)
355
			fprint(2, ">>> ");
356
	}
357
 
358
	if(wasraw)
359
		rawon();
360
	else
361
		rawoff();
362
	return 0;
363
}
364
 
365
void
366
post(char *srv, int fd)
367
{
368
	int f;
369
	char buf[32];
370
 
371
	f = create(srv, OWRITE /* |ORCLOSE */ , 0666);
372
	if(f < 0)
373
		sysfatal("create %s: %r", srv);
374
	snprint(buf, sizeof buf, "%d", fd);
375
	if(write(f, buf, strlen(buf)) != strlen(buf))
376
		sysfatal("write %s: %r", srv);
377
	close(f);
378
}
379
 
380
/*
381
 *  the real work.  two processes pass bytes back and forth between the
382
 *  terminal and the network.
383
 */
384
void
385
stdcon(int net)
386
{
387
	int netpid;
388
	int p[2];
389
	char *svc;
390
 
391
	svc = nil;
392
	if (srv) {
393
		if(pipe(p) < 0)
394
			sysfatal("pipe: %r");
395
		if (srv[0] != '/')
396
			svc = smprint("/srv/%s", srv);
397
		else
398
			svc = srv;
399
		post(svc, p[0]);
400
		close(p[0]);
401
		dup(p[1], 0);
402
		dup(p[1], 1);
403
		/* pipe is now std in & out */
404
	}
405
	ttypid = getpid();
406
	switch(netpid = rfork(RFMEM|RFPROC)){
407
	case -1:
408
		perror("con");
409
		exits("fork");
410
	case 0:
411
		notify(notifyf);
412
		fromnet(net);
413
		if (svc)
414
			remove(svc);
415
		postnote(PNPROC, ttypid, "die yankee dog");
416
		exits(0);
417
	default:
418
		notify(notifyf);
419
		fromkbd(net);
420
		if (svc)
421
			remove(svc);
422
		if(notkbd)
423
			for(;;)
424
				sleep(0);
425
		postnote(PNPROC, netpid, "die yankee dog");
426
		exits(0);
427
	}
428
}
429
 
430
/*
431
 *  Read the keyboard and write it to the network.  '^\' gets us into
432
 *  the menu.
433
 */
434
void
435
fromkbd(int net)
436
{
437
	long n;
438
	char buf[MAXMSG];
439
	char *p, *ep;
440
	int eofs;
441
 
442
	eofs = 0;
443
	for(;;){
444
		n = read(0, buf, sizeof(buf));
445
		if(n < 0){
446
			if(wasintr()){
447
				if(!raw){
448
					buf[0] = 0x7f;
449
					n = 1;
450
				} else
451
					continue;
452
			} else
453
				return;
454
		}
455
		if(n == 0){
456
			if(++eofs > 32)
457
				return;
458
		} else
459
			eofs = 0;
460
		if(n && memchr(buf, 0x1c, n)){
461
			if(menu(net) < 0)
462
				return;
463
		}else{
464
			if(!raw && n==0){
465
				buf[0] = 0x4;
466
				n = 1;
467
			}
468
			if(nltocr){
469
				ep = buf+n;
470
				for(p = buf; p < ep; p++)
471
					switch(*p){
472
					case '\r':
473
						*p = '\n';
474
						break;
475
					case '\n':
476
						*p = '\r';
477
						break;
478
					}
479
			}
480
			if(iwrite(net, buf, n) != n)
481
				return;
482
		}
483
	}
484
}
485
 
486
/*
487
 *  Read from the network and write to the screen.
488
 *  Filter out spurious carriage returns.
489
 */
490
void
491
fromnet(int net)
492
{
493
	long n;
494
	char buf[MAXMSG];
495
	char *cp, *ep;
496
 
497
	for(;;){
498
		n = iread(net, buf, sizeof(buf));
499
		if(n < 0)
500
			return;
501
		if(n == 0)
502
			continue;
503
 
504
		if (strip)
505
			for (cp=buf; cp<buf+n; cp++)
506
				*cp &= 0177;
507
 
508
		if(crtonl) {
509
			/* convert cr's to nl's */
510
			for (cp = buf; cp < buf + n; cp++)
511
				if (*cp == '\r')
512
					*cp = '\n';
513
		}
514
		else if(!returns){
515
			/* convert cr's to null's */
516
			cp = buf;
517
			ep = buf + n;
518
			while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){
519
				memmove(cp, cp+1, ep-cp-1);
520
				ep--;
521
				n--;
522
			}
523
		}
524
 
525
		if(n > 0 && iwrite(outfd, buf, n) != n){
526
			if(outfd == 1)
527
				return;
528
			outfd = 1;
529
			if(iwrite(1, buf, n) != n)
530
				return;
531
		}
532
	}
533
}
534
 
535
/*
536
 *  dial and return a data connection
537
 */
538
int
539
dodial(char *dest, char *net, char *service)
540
{
541
	char name[128];
542
	char devdir[128];
543
	int data;
544
 
545
	devdir[0] = 0;
546
	strcpy(name, netmkaddr(dest, net, service));
547
	data = dial(name, 0, devdir, &ctl);
548
	if(data < 0){
549
		seterr(name);
550
		return -1;
551
	}
552
	fprint(2, "connected to %s on %s\n", name, devdir);
553
	return data;
554
}
555
 
556
void
557
dosystem(int fd, char *cmd)
558
{
559
	char *p;
560
 
561
	p = system(fd, cmd);
562
	if(p){
563
		print("con: %s terminated with %s\n", cmd, p);
564
		exits(p);
565
	}
566
}
567
 
568
/*
569
 *  run a command with the network connection as standard IO
570
 */
571
char *
572
system(int fd, char *cmd)
573
{
574
	int pid;
575
	int p;
576
	static Waitmsg msg;
577
	int pfd[2];
578
	int n;
579
	char buf[4096];
580
 
581
	if(pipe(pfd) < 0){
582
		perror("pipe");
583
		return "pipe failed";
584
	}
585
	outfd = pfd[1];
586
 
587
	close(consctl);
588
	consctl = -1;
589
	switch(pid = fork()){
590
	case -1:
591
		perror("con");
592
		return "fork failed";
593
	case 0:
594
		close(pfd[1]);
595
		dup(pfd[0], 0);
596
		dup(fd, 1);
597
		close(ctl);
598
		close(fd);
599
		close(pfd[0]);
600
		if(*cmd)
601
			execl("/bin/rc", "rc", "-c", cmd, nil);
602
		else
603
			execl("/bin/rc", "rc", nil);
604
		perror("con");
605
		exits("exec");
606
		break;
607
	default:
608
		close(pfd[0]);
609
		while((n = read(pfd[1], buf, sizeof(buf))) > 0)
610
			if(write(fd, buf, n) != n)
611
				break;
612
		p = waitpid();
613
		outfd = 1;
614
		close(pfd[1]);
615
		if(p < 0 || p != pid)
616
			return "lost child";
617
		break;
618
	}
619
	return msg.msg;
620
}
621
 
622
int
623
wasintr(void)
624
{
625
	return strcmp(syserr(), "interrupted") == 0;
626
}
627
 
628
void
629
punt(char *msg)
630
{
631
	if(*msg == 0)
632
		msg = transerr;
633
	fprint(2, "con: %s\n", msg);
634
	exits(msg);
635
}
636
 
637
char*
638
syserr(void)
639
{
640
	static char err[ERRMAX];
641
	errstr(err, sizeof err);
642
	return err;
643
}
644
 
645
void
646
seterr(char *addr)
647
{
648
	char *se = syserr();
649
 
650
	if(verbose)
651
		fprint(2, "'%s' calling %s\n", se, addr);
652
	if(firsterr[0] && (strstr(se, "translate") ||
653
	 strstr(se, "file does not exist") ||
654
	 strstr(se, "unknown address") ||
655
	 strstr(se, "directory entry not found")))
656
		return;
657
	strcpy(firsterr, se);
658
}
659
 
660
 
661
long
662
iread(int f, void *a, int n)
663
{
664
	long m;
665
 
666
	for(;;){
667
		m = read(f, a, n);
668
		if(m >= 0 || !wasintr())
669
			break;
670
	}
671
	return m;
672
}
673
 
674
long
675
iwrite(int f, void *a, int n)
676
{
677
	long m;
678
 
679
	m = write(f, a, n);
680
	if(m < 0 && wasintr())
681
		return n;
682
	return m;
683
}