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