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 } 684