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*ERRLEN]; 20 char transerr[2*ERRLEN]; 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][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[2*NAMELEN]; 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[3*NAMELEN]; 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 sprint(cname, "%sctl", dest); 230 ctl = open(cname, ORDWR); 231 if(ctl >= 0 && baud > 0) 232 fprint(ctl, "b%d", baud); 233 234 if(cmd) 235 dosystem(net, cmd); 236 237 if(!cooked) 238 rawon(); 239 stdcon(net); 240 exits(0); 241 } 242 243 /* 244 * ignore interrupts 245 */ 246 void 247 notifyf(void *a, char *msg) 248 { 249 USED(a); 250 251 if(strstr(msg, "closed pipe") 252 || strcmp(msg, "interrupt") == 0 253 || strcmp(msg, "hangup") == 0) 254 noted(NCONT); 255 noted(NDFLT); 256 } 257 258 /* 259 * turn keyboard raw mode on 260 */ 261 void 262 rawon(void) 263 { 264 if(debug) 265 fprint(2, "rawon\n"); 266 if(raw) 267 return; 268 if(consctl < 0) 269 consctl = open("/dev/consctl", OWRITE); 270 if(consctl < 0){ 271 fprint(2, "can't open consctl\n"); 272 return; 273 } 274 write(consctl, "rawon", 5); 275 raw = 1; 276 } 277 278 /* 279 * turn keyboard raw mode off 280 */ 281 void 282 rawoff(void) 283 { 284 if(debug) 285 fprint(2, "rawoff\n"); 286 if(raw == 0) 287 return; 288 if(consctl < 0) 289 consctl = open("/dev/consctl", OWRITE); 290 if(consctl < 0){ 291 fprint(2, "can't open consctl\n"); 292 return; 293 } 294 write(consctl, "rawoff", 6); 295 raw = 0; 296 } 297 298 /* 299 * control menu 300 */ 301 #define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n" 302 303 int 304 menu(int net) 305 { 306 char buf[MAXMSG]; 307 long n; 308 int done; 309 int wasraw = raw; 310 311 if(wasraw) 312 rawoff(); 313 314 fprint(2, ">>> "); 315 for(done = 0; !done; ){ 316 n = read(0, buf, sizeof(buf)-1); 317 if(n <= 0) 318 return -1; 319 buf[n] = 0; 320 switch(buf[0]){ 321 case '!': 322 print(buf); 323 system(net, buf+1); 324 print("!\n"); 325 done = 1; 326 break; 327 case '.': 328 done = 1; 329 break; 330 case 'q': 331 return -1; 332 break; 333 case 'i': 334 buf[0] = 0x1c; 335 if(msgfd <= 0) 336 write(net, buf, 1); 337 else 338 sendctl1(msgfd, M_SIGNAL, SIGQUIT); 339 done = 1; 340 break; 341 case 'b': 342 if(msgfd >= 0) 343 sendctl(msgfd, M_BREAK); 344 else if(ctl >= 0) 345 write(ctl, "k", 1); 346 done = 1; 347 break; 348 case 'r': 349 returns = 1-returns; 350 done = 1; 351 break; 352 default: 353 fprint(2, STDHELP); 354 break; 355 } 356 if(!done) 357 fprint(2, ">>> "); 358 } 359 360 if(wasraw) 361 rawon(); 362 else 363 rawoff(); 364 return 0; 365 } 366 367 /* 368 * the real work. two processes pass bytes back and forth between the 369 * terminal and the network. 370 */ 371 void 372 stdcon(int net) 373 { 374 int netpid; 375 376 ttypid = getpid(); 377 switch(netpid = rfork(RFMEM|RFPROC)){ 378 case -1: 379 perror("con"); 380 exits("fork"); 381 case 0: 382 notify(notifyf); 383 fromnet(net); 384 postnote(PNPROC, ttypid, "kill"); 385 exits(0); 386 default: 387 notify(notifyf); 388 fromkbd(net); 389 if(notkbd) 390 for(;;)sleep(0); 391 postnote(PNPROC, netpid, "kill"); 392 exits(0); 393 } 394 } 395 396 /* 397 * Read the keyboard and write it to the network. '^\' gets us into 398 * the menu. 399 */ 400 void 401 fromkbd(int net) 402 { 403 long n; 404 char buf[MAXMSG]; 405 char *p, *ep; 406 int eofs; 407 408 eofs = 0; 409 for(;;){ 410 n = read(0, buf, sizeof(buf)); 411 if(n < 0){ 412 if(wasintr()){ 413 if(cooked){ 414 buf[0] = 0x7f; 415 n = 1; 416 } else 417 continue; 418 } else 419 return; 420 } 421 if(n == 0){ 422 if(++eofs > 32) 423 return; 424 } else 425 eofs = 0; 426 if(n && memchr(buf, 0x1c, n)){ 427 if(menu(net) < 0) 428 return; 429 }else{ 430 if(cooked && n==0){ 431 buf[0] = 0x4; 432 n = 1; 433 } 434 if(nltocr){ 435 ep = buf+n; 436 for(p = buf; p < ep; p++) 437 switch(*p){ 438 case '\r': 439 *p = '\n'; 440 break; 441 case '\n': 442 *p = '\r'; 443 break; 444 } 445 } 446 if(iwrite(net, buf, n) != n) 447 return; 448 } 449 } 450 } 451 452 /* 453 * Read from the network and write to the screen. 454 * Filter out spurious carriage returns. 455 */ 456 void 457 fromnet(int net) 458 { 459 long n; 460 char buf[MAXMSG]; 461 char *cp, *ep; 462 463 for(;;){ 464 n = iread(net, buf, sizeof(buf)); 465 if(n < 0) 466 return; 467 if(n == 0) 468 continue; 469 470 if (strip) 471 for (cp=buf; cp<buf+n; cp++) 472 *cp &= 0177; 473 474 if(!returns){ 475 /* convert cr's to null's */ 476 cp = buf; 477 ep = buf + n; 478 while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){ 479 memmove(cp, cp+1, ep-cp-1); 480 ep--; 481 n--; 482 } 483 } 484 485 if(n > 0 && iwrite(outfd, buf, n) != n){ 486 if(outfd == 1) 487 return; 488 outfd = 1; 489 if(iwrite(1, buf, n) != n) 490 return; 491 } 492 } 493 } 494 495 /* 496 * dial and return a data connection 497 */ 498 int 499 dodial(char *dest, char *net, char *service) 500 { 501 char name[3*NAMELEN]; 502 char devdir[3*NAMELEN]; 503 int data; 504 505 devdir[0] = 0; 506 strcpy(name, netmkaddr(dest, net, service)); 507 data = dial(name, 0, devdir, &ctl); 508 if(data < 0){ 509 seterr(name); 510 return -1; 511 } 512 fprint(2, "connected to %s on %s\n", name, devdir); 513 return data; 514 } 515 516 void 517 dosystem(int fd, char *cmd) 518 { 519 char *p; 520 521 p = system(fd, cmd); 522 if(*p){ 523 print("con: %s terminated with %s\n", cmd, p); 524 exits(p); 525 } 526 } 527 528 /* 529 * run a command with the network connection as standard IO 530 */ 531 char * 532 system(int fd, char *cmd) 533 { 534 int pid; 535 int p; 536 static Waitmsg msg; 537 int pfd[2]; 538 int n; 539 char buf[4096]; 540 541 if(pipe(pfd) < 0){ 542 perror("pipe"); 543 return "pipe failed"; 544 } 545 outfd = pfd[1]; 546 547 close(consctl); 548 consctl = -1; 549 switch(pid = fork()){ 550 case -1: 551 perror("con"); 552 return "fork failed"; 553 case 0: 554 close(pfd[1]); 555 dup(pfd[0], 0); 556 dup(pfd[0], 1); 557 close(ctl); 558 close(fd); 559 close(pfd[0]); 560 if(*cmd) 561 execl("/bin/rc", "rc", "-c", cmd, 0); 562 else 563 execl("/bin/rc", "rc", 0); 564 perror("con"); 565 exits("exec"); 566 break; 567 default: 568 close(pfd[0]); 569 while((n = read(pfd[1], buf, sizeof(buf))) > 0){ 570 if(msgfd >= 0){ 571 if(msgwrite(fd, buf, n) != n) 572 break; 573 } else { 574 if(write(fd, buf, n) != n) 575 break; 576 } 577 } 578 p = wait(&msg); 579 outfd = 1; 580 close(pfd[1]); 581 if(p < 0 || p != pid) 582 return "lost child"; 583 break; 584 } 585 return msg.msg; 586 } 587 588 int 589 wasintr(void) 590 { 591 return strcmp(syserr(), "interrupted") == 0; 592 } 593 594 void 595 punt(char *msg) 596 { 597 if(*msg == 0) 598 msg = transerr; 599 fprint(2, "con: %s\n", msg); 600 exits(msg); 601 } 602 603 char* 604 syserr(void) 605 { 606 static char err[ERRLEN]; 607 errstr(err); 608 return err; 609 } 610 611 void 612 seterr(char *addr) 613 { 614 char *se = syserr(); 615 616 if(verbose) 617 fprint(2, "'%s' calling %s\n", se, addr); 618 if(firsterr[0] && (strstr(se, "translate") || 619 strstr(se, "file does not exist") || 620 strstr(se, "unknown address") || 621 strstr(se, "directory entry not found"))) 622 return; 623 strcpy(firsterr, se); 624 } 625 626 627 long 628 iread(int f, void *a, int n) 629 { 630 long m; 631 632 for(;;){ 633 m = read(f, a, n); 634 if(m >= 0 || !wasintr()) 635 break; 636 } 637 return m; 638 } 639 640 long 641 iwrite(int f, void *a, int n) 642 { 643 long m; 644 645 m = write(f, a, n); 646 if(m < 0 && wasintr()) 647 return n; 648 return m; 649 } 650 651 /* 652 * The rest is to support the V10 mesgld protocol. 653 */ 654 655 /* 656 * network orderings 657 */ 658 #define get2byte(p) ((p)[0] + ((p)[1]<<8)) 659 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24)) 660 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8) 661 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24) 662 663 /* 664 * tty parameters 665 */ 666 int sgflags = ECHO; 667 668 /* 669 * a mesgld message 670 */ 671 struct Msg { 672 struct mesg h; 673 char b[MAXMSG]; 674 }; 675 676 /* 677 * convert certain interrupts into mesgld messages 678 */ 679 void 680 msgnotifyf(void *a, char *msg) 681 { 682 USED(a); 683 684 if(strstr(msg, "closed pipe")) 685 noted(NCONT); 686 if(strcmp(msg, "interrupt") == 0){ 687 sendctl1(msgfd, M_SIGNAL, SIGINT); 688 noted(NCONT); 689 } 690 if(strcmp(msg, "hangup") == 0){ 691 sendctl(msgfd, M_HANGUP); 692 noted(NCONT); 693 } 694 noted(NDFLT); 695 } 696 697 /* 698 * send an empty mesgld message 699 */ 700 int 701 sendctl(int net, int type) 702 { 703 Msg m; 704 705 m.h.type = type; 706 m.h.magic = MSGMAGIC; 707 put2byte(m.h.size, 0); 708 if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg)) 709 return -1; 710 return 0; 711 } 712 713 /* 714 * send a one byte mesgld message 715 */ 716 int 717 sendctl1(int net, int type, int parm) 718 { 719 Msg m; 720 721 m.h.type = type; 722 m.h.magic = MSGMAGIC; 723 m.b[0] = parm; 724 put2byte(m.h.size, 1); 725 if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1) 726 return -1; 727 return 0; 728 } 729 730 /* 731 * read n bytes. return -1 if it fails, 0 otherwise. 732 */ 733 int 734 readupto(int from, char *a, int len) 735 { 736 int n; 737 738 while(len > 0){ 739 n = iread(from, a, len); 740 if(n < 0) 741 return -1; 742 a += n; 743 len -= n; 744 } 745 return 0; 746 } 747 748 /* 749 * Decode a mesgld message from the network 750 */ 751 void 752 msgfromnet(int net) 753 { 754 ulong com; 755 struct stioctl *io; 756 struct sgttyb *sg; 757 struct ttydevb *td; 758 struct tchars *tc; 759 int len; 760 Msg m; 761 762 for(;;){ 763 /* get a complete mesgld message */ 764 if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0) 765 break; 766 if(m.h.magic != MSGMAGIC){ 767 fprint(2, "con: bad message magic 0x%ux\n", m.h.magic); 768 break; 769 } 770 len = get2byte(m.h.size); 771 if(len > sizeof(m.b)){ 772 len = sizeof(m.b); 773 fprint(2, "con: mesgld message too long\n"); 774 } 775 if(len && readupto(net, m.b, len) < 0) 776 break; 777 778 /* decode */ 779 switch(m.h.type){ 780 case M_HANGUP: 781 if(debug) 782 fprint(2, "M_HANGUP\n"); 783 return; 784 case M_DATA: 785 if(debug) 786 fprint(2, "M_DATA %d bytes\n", len); 787 if(iwrite(outfd, m.b, len) != len){ 788 if(outfd == 1) 789 return; 790 outfd = 1; 791 if(iwrite(outfd, m.b, len) != len) 792 return; 793 } 794 continue; 795 case M_IOCTL: 796 break; 797 default: 798 /* ignore */ 799 if(debug) 800 fprint(2, "con: unknown message\n"); 801 continue; 802 } 803 804 /* 805 * answer an ioctl 806 */ 807 io = (struct stioctl *)m.b; 808 com = get4byte(io->com); 809 if(debug) 810 fprint(2, "M_IOCTL %lud\n", com); 811 switch(com){ 812 case FIOLOOKLD: 813 put4byte(io->data, tty_ld); 814 len = 0; 815 break; 816 case TIOCGETP: 817 sg = (struct sgttyb *)io->data; 818 sg->sg_ispeed = sg->sg_ospeed = B9600; 819 sg->sg_erase = 0010; /* back space */ 820 sg->sg_kill = 0025; /* CNTL U */ 821 put2byte(sg->sg_flags, sgflags); 822 len = sizeof(struct sgttyb); 823 break; 824 case TIOCSETN: 825 case TIOCSETP: 826 sg = (struct sgttyb *)io->data; 827 sgflags = get2byte(sg->sg_flags); 828 if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO)) 829 rawon(); 830 else 831 rawoff(); 832 len = 0; 833 break; 834 case TIOCGETC: 835 tc = (struct tchars *)io->data; 836 tc->t_intrc = 0177; 837 tc->t_quitc = 0034; 838 tc->t_startc = 0; 839 tc->t_stopc = 0; 840 tc->t_eofc = 0004; 841 tc->t_brkc = 0; 842 len = sizeof(struct tchars); 843 break; 844 case TIOCSETC: 845 len = 0; 846 break; 847 case TIOCGDEV: 848 td = (struct ttydevb *)io->data; 849 td->ispeed = td->ospeed = B9600; 850 put2byte(td->flags, 0); 851 len = sizeof(struct ttydevb); 852 break; 853 case TIOCSDEV: 854 len = 0; 855 break; 856 default: 857 /* 858 * unimplemented 859 */ 860 m.b[len] = 0; 861 if(sendctl(net, M_IOCNAK) < 0) 862 return; 863 continue; 864 } 865 866 /* 867 * acknowledge 868 */ 869 m.h.type = M_IOCACK; 870 m.h.magic = MSGMAGIC; 871 len += 4; 872 put2byte(m.h.size, len); 873 len += sizeof(struct mesg); 874 if(iwrite(net, &m, len) != len) 875 return; 876 } 877 } 878 879 /* 880 * Read the keyboard, convert to mesgld messages, and write it to the network. 881 * '^\' gets us into the menu. 882 */ 883 void 884 msgfromkbd(int net) 885 { 886 long n; 887 char buf[MAXMSG]; 888 889 for(;;){ 890 n = iread(0, buf, sizeof(buf)); 891 if(n < 0) 892 return; 893 if(n && memchr(buf, 0034, n)){ 894 if(menu(net) < 0) 895 return; 896 } else { 897 if(msgwrite(net, buf, n) != n) 898 return; 899 } 900 } 901 } 902 903 int 904 msgwrite(int fd, void *buf, int len) 905 { 906 Msg m; 907 int n; 908 909 n = len; 910 memmove(m.b, buf, n); 911 put2byte(m.h.size, n); 912 m.h.magic = MSGMAGIC; 913 m.h.type = M_DATA; 914 n += sizeof(struct mesg); 915 if(iwrite(fd, &m, n) != n) 916 return -1; 917 918 put2byte(m.h.size, 0); 919 m.h.magic = MSGMAGIC; 920 m.h.type = M_DELIM; 921 n = sizeof(struct mesg); 922 if(iwrite(fd, &m, n) != n) 923 return -1; 924 925 return len; 926 } 927 928