1 #include <u.h> 2 #include <libc.h> 3 4 #include "rustream.h" 5 #include "ruttyio.h" 6 #include "rusignal.h" 7 #include "rufilio.h" 8 9 int debug; /* true if debugging */ 10 int ctl = -1; /* control fd (for break's) */ 11 int raw; /* true if raw is on */ 12 int consctl = -1; /* control fd for cons */ 13 int ttypid; /* pid's if the 2 processes (used to kill them) */ 14 int msgfd = -1; /* mesgld file descriptor (for signals to be written to) */ 15 int outfd = 1; /* local output file descriptor */ 16 int cooked; /* non-zero forces cooked mode */ 17 int returns; /* non-zero forces carriage returns not to be filtered out */ 18 int crtonl; /* non-zero forces carriage returns to be converted to nls coming from net */ 19 int strip; /* strip off parity bits */ 20 char firsterr[2*ERRMAX]; 21 char transerr[2*ERRMAX]; 22 int limited; 23 char *remuser; 24 int verbose; 25 int baud; 26 int notkbd; 27 int nltocr; /* translate kbd nl to cr and vice versa */ 28 29 typedef struct Msg Msg; 30 #define MAXMSG (2*8192) 31 32 int dodial(char*, char*, char*); 33 void fromkbd(int); 34 void fromnet(int); 35 long iread(int, void*, int); 36 long iwrite(int, void*, int); 37 int menu(int); 38 void msgfromkbd(int); 39 void msgfromnet(int); 40 void msgnotifyf(void*, char*); 41 int msgwrite(int, void*, int); 42 void notifyf(void*, char*); 43 void pass(int, int, int); 44 void rawoff(void); 45 void rawon(void); 46 int readupto(int, char*, int); 47 int sendctl(int, int); 48 int sendctl1(int, int, int); 49 void stdcon(int); 50 char* system(int, char*); 51 void dosystem(int, char*); 52 int wasintr(void); 53 void punt(char*); 54 char* syserr(void); 55 void seterr(char*); 56 57 /* protocols */ 58 void device(char*, char*); 59 void rlogin(char*, char*); 60 void simple(char*, char*); 61 62 void 63 usage(void) 64 { 65 punt("usage: con [-drCvsn] [-l [user]] [-c cmd] net!host[!service]"); 66 } 67 68 void 69 main(int argc, char *argv[]) 70 { 71 char *dest; 72 char *cmd = 0; 73 74 returns = 1; 75 ARGBEGIN{ 76 case 'b': 77 baud = atoi(ARGF()); 78 break; 79 case 'd': 80 debug = 1; 81 break; 82 case 'l': 83 limited = 1; 84 if(argv[1] != nil && argv[1][0] != '-') 85 remuser = ARGF(); 86 break; 87 case 'n': 88 notkbd = 1; 89 break; 90 case 'r': 91 returns = 0; 92 break; 93 case 'R': 94 nltocr = 1; 95 break; 96 case 'T': 97 crtonl = 1; 98 break; 99 case 'C': 100 cooked = 1; 101 break; 102 case 'c': 103 cmd = ARGF(); 104 break; 105 case 'v': 106 verbose = 1; 107 break; 108 case 's': 109 strip = 1; 110 break; 111 default: 112 usage(); 113 }ARGEND 114 115 if(argc != 1){ 116 if(remuser == 0) 117 usage(); 118 dest = remuser; 119 remuser = 0; 120 } else 121 dest = argv[0]; 122 if(*dest == '/' && strchr(dest, '!') == 0) 123 device(dest, cmd); 124 else if(limited){ 125 simple(dest, cmd); /* doesn't return if dialout succeeds */ 126 rlogin(dest, cmd); /* doesn't return if dialout succeeds */ 127 } else { 128 rlogin(dest, cmd); /* doesn't return if dialout succeeds */ 129 simple(dest, cmd); /* doesn't return if dialout succeeds */ 130 } 131 punt(firsterr); 132 } 133 134 /* 135 * just dial and use as a byte stream with remote echo 136 */ 137 void 138 simple(char *dest, char *cmd) 139 { 140 int net; 141 142 net = dodial(dest, 0, 0); 143 if(net < 0) 144 return; 145 146 if(cmd) 147 dosystem(net, cmd); 148 149 if(!cooked) 150 rawon(); 151 stdcon(net); 152 exits(0); 153 } 154 155 /* 156 * dial, do UCB authentication, use as a byte stream with local echo 157 * 158 * return if dial failed 159 */ 160 void 161 rlogin(char *dest, char *cmd) 162 { 163 int net; 164 char buf[128]; 165 char *p; 166 char *localuser; 167 168 /* only useful on TCP */ 169 if(strchr(dest, '!') 170 && (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0)) 171 return; 172 173 net = dodial(dest, "tcp", "login"); 174 if(net < 0) 175 return; 176 177 /* 178 * do UCB rlogin authentication 179 */ 180 localuser = getuser(); 181 if(remuser == 0){ 182 if(limited) 183 remuser = ":"; 184 else 185 remuser = localuser; 186 } 187 p = getenv("TERM"); 188 if(p == 0) 189 p = "p9"; 190 if(write(net, "", 1)<0 191 || write(net, localuser, strlen(localuser)+1)<0 192 || write(net, remuser, strlen(remuser)+1)<0 193 || write(net, p, strlen(p)+1)<0){ 194 close(net); 195 punt("BSD authentication failed"); 196 } 197 if(read(net, buf, 1) != 1) 198 punt("BSD authentication failed1"); 199 if(buf[0] != 0){ 200 fprint(2, "con: remote error: "); 201 while(read(net, buf, 1) == 1){ 202 write(2, buf, 1); 203 if(buf[0] == '\n') 204 break; 205 } 206 exits("read"); 207 } 208 209 if(cmd) 210 dosystem(net, cmd); 211 212 if(!cooked) 213 rawon(); 214 nltocr = 1; 215 stdcon(net); 216 exits(0); 217 } 218 219 /* 220 * just open a device and use it as a connection 221 */ 222 void 223 device(char *dest, char *cmd) 224 { 225 int net; 226 char cname[128]; 227 228 net = open(dest, ORDWR); 229 if(net < 0) { 230 fprint(2, "con: cannot open %s: %r\n", dest); 231 exits("open"); 232 } 233 snprint(cname, sizeof cname, "%sctl", dest); 234 ctl = open(cname, ORDWR); 235 if (baud > 0) { 236 if(ctl >= 0) 237 fprint(ctl, "b%d", baud); 238 else 239 fprint(2, "con: cannot open %s: %r\n", cname); 240 } 241 242 if(cmd) 243 dosystem(net, cmd); 244 245 if(!cooked) 246 rawon(); 247 stdcon(net); 248 exits(0); 249 } 250 251 /* 252 * ignore interrupts 253 */ 254 void 255 notifyf(void *a, char *msg) 256 { 257 USED(a); 258 259 if(strstr(msg, "closed pipe") 260 || strcmp(msg, "interrupt") == 0 261 || strcmp(msg, "hangup") == 0) 262 noted(NCONT); 263 noted(NDFLT); 264 } 265 266 /* 267 * turn keyboard raw mode on 268 */ 269 void 270 rawon(void) 271 { 272 if(debug) 273 fprint(2, "rawon\n"); 274 if(raw) 275 return; 276 if(consctl < 0) 277 consctl = open("/dev/consctl", OWRITE); 278 if(consctl < 0){ 279 // fprint(2, "can't open consctl\n"); 280 return; 281 } 282 write(consctl, "rawon", 5); 283 raw = 1; 284 } 285 286 /* 287 * turn keyboard raw mode off 288 */ 289 void 290 rawoff(void) 291 { 292 if(debug) 293 fprint(2, "rawoff\n"); 294 if(raw == 0) 295 return; 296 if(consctl < 0) 297 consctl = open("/dev/consctl", OWRITE); 298 if(consctl < 0){ 299 // fprint(2, "can't open consctl\n"); 300 return; 301 } 302 write(consctl, "rawoff", 6); 303 raw = 0; 304 } 305 306 /* 307 * control menu 308 */ 309 #define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n" 310 311 int 312 menu(int net) 313 { 314 char buf[MAXMSG]; 315 long n; 316 int done; 317 int wasraw = raw; 318 319 if(wasraw) 320 rawoff(); 321 322 fprint(2, ">>> "); 323 for(done = 0; !done; ){ 324 n = read(0, buf, sizeof(buf)-1); 325 if(n <= 0) 326 return -1; 327 buf[n] = 0; 328 switch(buf[0]){ 329 case '!': 330 print(buf); 331 system(net, buf+1); 332 print("!\n"); 333 done = 1; 334 break; 335 case '.': 336 done = 1; 337 break; 338 case 'q': 339 return -1; 340 break; 341 case 'i': 342 buf[0] = 0x1c; 343 if(msgfd <= 0) 344 write(net, buf, 1); 345 else 346 sendctl1(msgfd, M_SIGNAL, SIGQUIT); 347 done = 1; 348 break; 349 case 'b': 350 if(msgfd >= 0) 351 sendctl(msgfd, M_BREAK); 352 else if(ctl >= 0) 353 write(ctl, "k", 1); 354 done = 1; 355 break; 356 case 'r': 357 returns = 1-returns; 358 done = 1; 359 break; 360 default: 361 fprint(2, STDHELP); 362 break; 363 } 364 if(!done) 365 fprint(2, ">>> "); 366 } 367 368 if(wasraw) 369 rawon(); 370 else 371 rawoff(); 372 return 0; 373 } 374 375 /* 376 * the real work. two processes pass bytes back and forth between the 377 * terminal and the network. 378 */ 379 void 380 stdcon(int net) 381 { 382 int netpid; 383 384 ttypid = getpid(); 385 switch(netpid = rfork(RFMEM|RFPROC)){ 386 case -1: 387 perror("con"); 388 exits("fork"); 389 case 0: 390 notify(notifyf); 391 fromnet(net); 392 postnote(PNPROC, ttypid, ""); 393 exits(0); 394 default: 395 notify(notifyf); 396 fromkbd(net); 397 if(notkbd) 398 for(;;)sleep(0); 399 postnote(PNPROC, netpid, ""); 400 exits(0); 401 } 402 } 403 404 /* 405 * Read the keyboard and write it to the network. '^\' gets us into 406 * the menu. 407 */ 408 void 409 fromkbd(int net) 410 { 411 long n; 412 char buf[MAXMSG]; 413 char *p, *ep; 414 int eofs; 415 416 eofs = 0; 417 for(;;){ 418 n = read(0, buf, sizeof(buf)); 419 if(n < 0){ 420 if(wasintr()){ 421 if(cooked){ 422 buf[0] = 0x7f; 423 n = 1; 424 } else 425 continue; 426 } else 427 return; 428 } 429 if(n == 0){ 430 if(++eofs > 32) 431 return; 432 } else 433 eofs = 0; 434 if(n && memchr(buf, 0x1c, n)){ 435 if(menu(net) < 0) 436 return; 437 }else{ 438 if(cooked && n==0){ 439 buf[0] = 0x4; 440 n = 1; 441 } 442 if(nltocr){ 443 ep = buf+n; 444 for(p = buf; p < ep; p++) 445 switch(*p){ 446 case '\r': 447 *p = '\n'; 448 break; 449 case '\n': 450 *p = '\r'; 451 break; 452 } 453 } 454 if(iwrite(net, buf, n) != n) 455 return; 456 } 457 } 458 } 459 460 /* 461 * Read from the network and write to the screen. 462 * Filter out spurious carriage returns. 463 */ 464 void 465 fromnet(int net) 466 { 467 long n; 468 char buf[MAXMSG]; 469 char *cp, *ep; 470 471 for(;;){ 472 n = iread(net, buf, sizeof(buf)); 473 if(n < 0) 474 return; 475 if(n == 0) 476 continue; 477 478 if (strip) 479 for (cp=buf; cp<buf+n; cp++) 480 *cp &= 0177; 481 482 if(crtonl) { 483 /* convert cr's to nl's */ 484 for (cp = buf; cp < buf + n; cp++) 485 if (*cp == '\r') 486 *cp = '\n'; 487 } 488 else if(!returns){ 489 /* convert cr's to null's */ 490 cp = buf; 491 ep = buf + n; 492 while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){ 493 memmove(cp, cp+1, ep-cp-1); 494 ep--; 495 n--; 496 } 497 } 498 499 if(n > 0 && iwrite(outfd, buf, n) != n){ 500 if(outfd == 1) 501 return; 502 outfd = 1; 503 if(iwrite(1, buf, n) != n) 504 return; 505 } 506 } 507 } 508 509 /* 510 * dial and return a data connection 511 */ 512 int 513 dodial(char *dest, char *net, char *service) 514 { 515 char name[128]; 516 char devdir[128]; 517 int data; 518 519 devdir[0] = 0; 520 strcpy(name, netmkaddr(dest, net, service)); 521 data = dial(name, 0, devdir, &ctl); 522 if(data < 0){ 523 seterr(name); 524 return -1; 525 } 526 fprint(2, "connected to %s on %s\n", name, devdir); 527 return data; 528 } 529 530 void 531 dosystem(int fd, char *cmd) 532 { 533 char *p; 534 535 p = system(fd, cmd); 536 if(*p){ 537 print("con: %s terminated with %s\n", cmd, p); 538 exits(p); 539 } 540 } 541 542 /* 543 * run a command with the network connection as standard IO 544 */ 545 char * 546 system(int fd, char *cmd) 547 { 548 int pid; 549 int p; 550 static Waitmsg msg; 551 int pfd[2]; 552 int n; 553 char buf[4096]; 554 555 if(pipe(pfd) < 0){ 556 perror("pipe"); 557 return "pipe failed"; 558 } 559 outfd = pfd[1]; 560 561 close(consctl); 562 consctl = -1; 563 switch(pid = fork()){ 564 case -1: 565 perror("con"); 566 return "fork failed"; 567 case 0: 568 close(pfd[1]); 569 dup(pfd[0], 0); 570 dup(fd, 1); 571 close(ctl); 572 close(fd); 573 close(pfd[0]); 574 if(*cmd) 575 execl("/bin/rc", "rc", "-c", cmd, 0); 576 else 577 execl("/bin/rc", "rc", 0); 578 perror("con"); 579 exits("exec"); 580 break; 581 default: 582 close(pfd[0]); 583 while((n = read(pfd[1], buf, sizeof(buf))) > 0){ 584 if(msgfd >= 0){ 585 if(msgwrite(fd, buf, n) != n) 586 break; 587 } else { 588 if(write(fd, buf, n) != n) 589 break; 590 } 591 } 592 p = waitpid(); 593 outfd = 1; 594 close(pfd[1]); 595 if(p < 0 || p != pid) 596 return "lost child"; 597 break; 598 } 599 return msg.msg; 600 } 601 602 int 603 wasintr(void) 604 { 605 return strcmp(syserr(), "interrupted") == 0; 606 } 607 608 void 609 punt(char *msg) 610 { 611 if(*msg == 0) 612 msg = transerr; 613 fprint(2, "con: %s\n", msg); 614 exits(msg); 615 } 616 617 char* 618 syserr(void) 619 { 620 static char err[ERRMAX]; 621 errstr(err, sizeof err); 622 return err; 623 } 624 625 void 626 seterr(char *addr) 627 { 628 char *se = syserr(); 629 630 if(verbose) 631 fprint(2, "'%s' calling %s\n", se, addr); 632 if(firsterr[0] && (strstr(se, "translate") || 633 strstr(se, "file does not exist") || 634 strstr(se, "unknown address") || 635 strstr(se, "directory entry not found"))) 636 return; 637 strcpy(firsterr, se); 638 } 639 640 641 long 642 iread(int f, void *a, int n) 643 { 644 long m; 645 646 for(;;){ 647 m = read(f, a, n); 648 if(m >= 0 || !wasintr()) 649 break; 650 } 651 return m; 652 } 653 654 long 655 iwrite(int f, void *a, int n) 656 { 657 long m; 658 659 m = write(f, a, n); 660 if(m < 0 && wasintr()) 661 return n; 662 return m; 663 } 664 665 /* 666 * The rest is to support the V10 mesgld protocol. 667 */ 668 669 /* 670 * network orderings 671 */ 672 #define get2byte(p) ((p)[0] + ((p)[1]<<8)) 673 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24)) 674 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8) 675 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24) 676 677 /* 678 * tty parameters 679 */ 680 int sgflags = ECHO; 681 682 /* 683 * a mesgld message 684 */ 685 struct Msg { 686 struct mesg h; 687 char b[MAXMSG]; 688 }; 689 690 /* 691 * convert certain interrupts into mesgld messages 692 */ 693 void 694 msgnotifyf(void *a, char *msg) 695 { 696 USED(a); 697 698 if(strstr(msg, "closed pipe")) 699 noted(NCONT); 700 if(strcmp(msg, "interrupt") == 0){ 701 sendctl1(msgfd, M_SIGNAL, SIGINT); 702 noted(NCONT); 703 } 704 if(strcmp(msg, "hangup") == 0){ 705 sendctl(msgfd, M_HANGUP); 706 noted(NCONT); 707 } 708 noted(NDFLT); 709 } 710 711 /* 712 * send an empty mesgld message 713 */ 714 int 715 sendctl(int net, int type) 716 { 717 Msg m; 718 719 m.h.type = type; 720 m.h.magic = MSGMAGIC; 721 put2byte(m.h.size, 0); 722 if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg)) 723 return -1; 724 return 0; 725 } 726 727 /* 728 * send a one byte mesgld message 729 */ 730 int 731 sendctl1(int net, int type, int parm) 732 { 733 Msg m; 734 735 m.h.type = type; 736 m.h.magic = MSGMAGIC; 737 m.b[0] = parm; 738 put2byte(m.h.size, 1); 739 if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1) 740 return -1; 741 return 0; 742 } 743 744 /* 745 * read n bytes. return -1 if it fails, 0 otherwise. 746 */ 747 int 748 readupto(int from, char *a, int len) 749 { 750 int n; 751 752 while(len > 0){ 753 n = iread(from, a, len); 754 if(n < 0) 755 return -1; 756 a += n; 757 len -= n; 758 } 759 return 0; 760 } 761 762 /* 763 * Decode a mesgld message from the network 764 */ 765 void 766 msgfromnet(int net) 767 { 768 ulong com; 769 struct stioctl *io; 770 struct sgttyb *sg; 771 struct ttydevb *td; 772 struct tchars *tc; 773 int len; 774 Msg m; 775 776 for(;;){ 777 /* get a complete mesgld message */ 778 if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0) 779 break; 780 if(m.h.magic != MSGMAGIC){ 781 fprint(2, "con: bad message magic 0x%ux\n", m.h.magic); 782 break; 783 } 784 len = get2byte(m.h.size); 785 if(len > sizeof(m.b)){ 786 len = sizeof(m.b); 787 fprint(2, "con: mesgld message too long\n"); 788 } 789 if(len && readupto(net, m.b, len) < 0) 790 break; 791 792 /* decode */ 793 switch(m.h.type){ 794 case M_HANGUP: 795 if(debug) 796 fprint(2, "M_HANGUP\n"); 797 return; 798 case M_DATA: 799 if(debug) 800 fprint(2, "M_DATA %d bytes\n", len); 801 if(iwrite(outfd, m.b, len) != len){ 802 if(outfd == 1) 803 return; 804 outfd = 1; 805 if(iwrite(outfd, m.b, len) != len) 806 return; 807 } 808 continue; 809 case M_IOCTL: 810 break; 811 default: 812 /* ignore */ 813 if(debug) 814 fprint(2, "con: unknown message\n"); 815 continue; 816 } 817 818 /* 819 * answer an ioctl 820 */ 821 io = (struct stioctl *)m.b; 822 com = get4byte(io->com); 823 if(debug) 824 fprint(2, "M_IOCTL %lud\n", com); 825 switch(com){ 826 case FIOLOOKLD: 827 put4byte(io->data, tty_ld); 828 len = 0; 829 break; 830 case TIOCGETP: 831 sg = (struct sgttyb *)io->data; 832 sg->sg_ispeed = sg->sg_ospeed = B9600; 833 sg->sg_erase = 0010; /* back space */ 834 sg->sg_kill = 0025; /* CNTL U */ 835 put2byte(sg->sg_flags, sgflags); 836 len = sizeof(struct sgttyb); 837 break; 838 case TIOCSETN: 839 case TIOCSETP: 840 sg = (struct sgttyb *)io->data; 841 sgflags = get2byte(sg->sg_flags); 842 if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO)) 843 rawon(); 844 else 845 rawoff(); 846 len = 0; 847 break; 848 case TIOCGETC: 849 tc = (struct tchars *)io->data; 850 tc->t_intrc = 0177; 851 tc->t_quitc = 0034; 852 tc->t_startc = 0; 853 tc->t_stopc = 0; 854 tc->t_eofc = 0004; 855 tc->t_brkc = 0; 856 len = sizeof(struct tchars); 857 break; 858 case TIOCSETC: 859 len = 0; 860 break; 861 case TIOCGDEV: 862 td = (struct ttydevb *)io->data; 863 td->ispeed = td->ospeed = B9600; 864 put2byte(td->flags, 0); 865 len = sizeof(struct ttydevb); 866 break; 867 case TIOCSDEV: 868 len = 0; 869 break; 870 default: 871 /* 872 * unimplemented 873 */ 874 m.b[len] = 0; 875 if(sendctl(net, M_IOCNAK) < 0) 876 return; 877 continue; 878 } 879 880 /* 881 * acknowledge 882 */ 883 m.h.type = M_IOCACK; 884 m.h.magic = MSGMAGIC; 885 len += 4; 886 put2byte(m.h.size, len); 887 len += sizeof(struct mesg); 888 if(iwrite(net, &m, len) != len) 889 return; 890 } 891 } 892 893 /* 894 * Read the keyboard, convert to mesgld messages, and write it to the network. 895 * '^\' gets us into the menu. 896 */ 897 void 898 msgfromkbd(int net) 899 { 900 long n; 901 char buf[MAXMSG]; 902 903 for(;;){ 904 n = iread(0, buf, sizeof(buf)); 905 if(n < 0) 906 return; 907 if(n && memchr(buf, 0034, n)){ 908 if(menu(net) < 0) 909 return; 910 } else { 911 if(msgwrite(net, buf, n) != n) 912 return; 913 } 914 } 915 } 916 917 int 918 msgwrite(int fd, void *buf, int len) 919 { 920 Msg m; 921 int n; 922 923 n = len; 924 memmove(m.b, buf, n); 925 put2byte(m.h.size, n); 926 m.h.magic = MSGMAGIC; 927 m.h.type = M_DATA; 928 n += sizeof(struct mesg); 929 if(iwrite(fd, &m, n) != n) 930 return -1; 931 932 put2byte(m.h.size, 0); 933 m.h.magic = MSGMAGIC; 934 m.h.type = M_DELIM; 935 n = sizeof(struct mesg); 936 if(iwrite(fd, &m, n) != n) 937 return -1; 938 939 return len; 940 } 941 942