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
usage(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
main(int argc,char * argv[])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
simple(char * dest,char * cmd)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
rlogin(char * dest,char * cmd)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
device(char * dest,char * cmd)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
notifyf(void * a,char * msg)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
rawon(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
rawoff(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
menu(int net)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
post(char * srv,int fd)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
stdcon(int net)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
fromkbd(int net)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
fromnet(int net)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
dodial(char * dest,char * net,char * service)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
dosystem(int fd,char * cmd)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 *
system(int fd,char * cmd)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
wasintr(void)623 wasintr(void)
624 {
625 return strcmp(syserr(), "interrupted") == 0;
626 }
627
628 void
punt(char * msg)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*
syserr(void)638 syserr(void)
639 {
640 static char err[ERRMAX];
641 errstr(err, sizeof err);
642 return err;
643 }
644
645 void
seterr(char * addr)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
iread(int f,void * a,int n)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
iwrite(int f,void * a,int n)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 }
684