1 /* $NetBSD: sys_bsd.c,v 1.31 2004/03/20 23:26:05 heas Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 from: static char sccsid[] = "@(#)sys_bsd.c 8.4 (Berkeley) 5/30/95"; 36 #else 37 __RCSID("$NetBSD: sys_bsd.c,v 1.31 2004/03/20 23:26:05 heas Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 /* 42 * The following routines try to encapsulate what is system dependent 43 * (at least between 4.x and dos) which is used in telnet.c. 44 */ 45 46 47 #include <fcntl.h> 48 #include <sys/types.h> 49 #include <sys/time.h> 50 #include <sys/socket.h> 51 #include <signal.h> 52 #include <stdlib.h> 53 #include <unistd.h> 54 #include <errno.h> 55 #include <poll.h> 56 #include <arpa/telnet.h> 57 58 #include "ring.h" 59 #include "defines.h" 60 #include "externs.h" 61 #include "types.h" 62 63 #define SIG_FUNC_RET void 64 65 SIG_FUNC_RET susp(int); 66 SIG_FUNC_RET ayt(int); 67 68 SIG_FUNC_RET intr(int); 69 SIG_FUNC_RET intr2(int); 70 SIG_FUNC_RET sendwin(int); 71 SIG_FUNC_RET deadpeer(int); 72 73 74 int 75 tout, /* Output file descriptor */ 76 tin, /* Input file descriptor */ 77 net; 78 79 struct termios old_tc = { 0 }; 80 extern struct termios new_tc; 81 82 # ifndef TCSANOW 83 # ifdef TCSETS 84 # define TCSANOW TCSETS 85 # define TCSADRAIN TCSETSW 86 # define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) 87 # else 88 # ifdef TCSETA 89 # define TCSANOW TCSETA 90 # define TCSADRAIN TCSETAW 91 # define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) 92 # else 93 # define TCSANOW TIOCSETA 94 # define TCSADRAIN TIOCSETAW 95 # define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) 96 # endif 97 # endif 98 # define tcsetattr(f, a, t) ioctl(f, a, (char *)t) 99 # define cfgetospeed(ptr) ((ptr)->c_cflag&CBAUD) 100 # ifdef CIBAUD 101 # define cfgetispeed(ptr) (((ptr)->c_cflag&CIBAUD) >> IBSHIFT) 102 # else 103 # define cfgetispeed(ptr) cfgetospeed(ptr) 104 # endif 105 # endif /* TCSANOW */ 106 107 108 void 109 init_sys(void) 110 { 111 tout = fileno(stdout); 112 tin = fileno(stdin); 113 114 errno = 0; 115 } 116 117 118 int 119 TerminalWrite(char *buf, int n) 120 { 121 return write(tout, buf, n); 122 } 123 124 int 125 TerminalRead(unsigned char *buf, int n) 126 { 127 return read(tin, buf, n); 128 } 129 130 /* 131 * 132 */ 133 134 int 135 TerminalAutoFlush(void) 136 { 137 return 1; 138 } 139 140 #ifdef KLUDGELINEMODE 141 extern int kludgelinemode; 142 #endif 143 /* 144 * TerminalSpecialChars() 145 * 146 * Look at an input character to see if it is a special character 147 * and decide what to do. 148 * 149 * Output: 150 * 151 * 0 Don't add this character. 152 * 1 Do add this character 153 */ 154 155 int 156 TerminalSpecialChars(int c) 157 { 158 if (c == termIntChar) { 159 intp(); 160 return 0; 161 } else if (c == termQuitChar) { 162 #ifdef KLUDGELINEMODE 163 if (kludgelinemode) 164 sendbrk(); 165 else 166 #endif 167 sendabort(); 168 return 0; 169 } else if (c == termEofChar) { 170 if (my_want_state_is_will(TELOPT_LINEMODE)) { 171 sendeof(); 172 return 0; 173 } 174 return 1; 175 } else if (c == termSuspChar) { 176 sendsusp(); 177 return(0); 178 } else if (c == termFlushChar) { 179 xmitAO(); /* Transmit Abort Output */ 180 return 0; 181 } else if (!MODE_LOCAL_CHARS(globalmode)) { 182 if (c == termKillChar) { 183 xmitEL(); 184 return 0; 185 } else if (c == termEraseChar) { 186 xmitEC(); /* Transmit Erase Character */ 187 return 0; 188 } 189 } 190 return 1; 191 } 192 193 194 /* 195 * Flush output to the terminal 196 */ 197 198 void 199 TerminalFlushOutput(void) 200 { 201 int com = 0; 202 (void) ioctl(fileno(stdout), TIOCFLUSH, &com); 203 } 204 205 void 206 TerminalSaveState(void) 207 { 208 tcgetattr(0, &old_tc); 209 210 new_tc = old_tc; 211 } 212 213 cc_t * 214 tcval(int func) 215 { 216 switch(func) { 217 case SLC_IP: return(&termIntChar); 218 case SLC_ABORT: return(&termQuitChar); 219 case SLC_EOF: return(&termEofChar); 220 case SLC_EC: return(&termEraseChar); 221 case SLC_EL: return(&termKillChar); 222 case SLC_XON: return(&termStartChar); 223 case SLC_XOFF: return(&termStopChar); 224 case SLC_FORW1: return(&termForw1Char); 225 case SLC_FORW2: return(&termForw2Char); 226 case SLC_AO: return(&termFlushChar); 227 case SLC_SUSP: return(&termSuspChar); 228 case SLC_EW: return(&termWerasChar); 229 case SLC_RP: return(&termRprntChar); 230 case SLC_LNEXT: return(&termLiteralNextChar); 231 case SLC_AYT: return(&termAytChar); 232 233 case SLC_SYNCH: 234 case SLC_BRK: 235 case SLC_EOR: 236 default: 237 return((cc_t *)0); 238 } 239 } 240 241 void 242 TerminalDefaultChars(void) 243 { 244 memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc)); 245 } 246 247 #ifdef notdef 248 void 249 TerminalRestoreState(void) 250 { 251 } 252 #endif 253 254 /* 255 * TerminalNewMode - set up terminal to a specific mode. 256 * MODE_ECHO: do local terminal echo 257 * MODE_FLOW: do local flow control 258 * MODE_TRAPSIG: do local mapping to TELNET IAC sequences 259 * MODE_EDIT: do local line editing 260 * 261 * Command mode: 262 * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG 263 * local echo 264 * local editing 265 * local xon/xoff 266 * local signal mapping 267 * 268 * Linemode: 269 * local/no editing 270 * Both Linemode and Single Character mode: 271 * local/remote echo 272 * local/no xon/xoff 273 * local/no signal mapping 274 */ 275 276 277 void 278 TerminalNewMode(int f) 279 { 280 static int prevmode = 0; 281 struct termios tmp_tc; 282 int onoff; 283 int old; 284 cc_t esc; 285 286 globalmode = f&~MODE_FORCE; 287 if (prevmode == f) 288 return; 289 290 /* 291 * Write any outstanding data before switching modes 292 * ttyflush() returns 0 only when there is no more data 293 * left to write out, it returns -1 if it couldn't do 294 * anything at all, otherwise it returns 1 + the number 295 * of characters left to write. 296 #ifndef USE_TERMIO 297 * We would really like to ask the kernel to wait for the output 298 * to drain, like we can do with the TCSADRAIN, but we don't have 299 * that option. The only ioctl that waits for the output to 300 * drain, TIOCSETP, also flushes the input queue, which is NOT 301 * what we want (TIOCSETP is like TCSADFLUSH). 302 #endif 303 */ 304 old = ttyflush(SYNCHing|flushout); 305 if (old < 0 || old > 1) { 306 tcgetattr(tin, &tmp_tc); 307 do { 308 /* 309 * Wait for data to drain, then flush again. 310 */ 311 tcsetattr(tin, TCSADRAIN, &tmp_tc); 312 old = ttyflush(SYNCHing|flushout); 313 } while (old < 0 || old > 1); 314 } 315 316 old = prevmode; 317 prevmode = f&~MODE_FORCE; 318 tmp_tc = new_tc; 319 320 if (f&MODE_ECHO) { 321 tmp_tc.c_lflag |= ECHO; 322 tmp_tc.c_oflag |= ONLCR; 323 if (crlf) 324 tmp_tc.c_iflag |= ICRNL; 325 } else { 326 tmp_tc.c_lflag &= ~ECHO; 327 tmp_tc.c_oflag &= ~ONLCR; 328 # ifdef notdef 329 if (crlf) 330 tmp_tc.c_iflag &= ~ICRNL; 331 # endif 332 } 333 334 if ((f&MODE_FLOW) == 0) { 335 tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ 336 } else { 337 if (restartany < 0) { 338 tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */ 339 } else if (restartany > 0) { 340 tmp_tc.c_iflag |= IXOFF|IXON|IXANY; 341 } else { 342 tmp_tc.c_iflag |= IXOFF|IXON; 343 tmp_tc.c_iflag &= ~IXANY; 344 } 345 } 346 347 if ((f&MODE_TRAPSIG) == 0) { 348 tmp_tc.c_lflag &= ~ISIG; 349 localchars = 0; 350 } else { 351 tmp_tc.c_lflag |= ISIG; 352 localchars = 1; 353 } 354 355 if (f&MODE_EDIT) { 356 tmp_tc.c_lflag |= ICANON; 357 } else { 358 tmp_tc.c_lflag &= ~ICANON; 359 tmp_tc.c_iflag &= ~ICRNL; 360 tmp_tc.c_cc[VMIN] = 1; 361 tmp_tc.c_cc[VTIME] = 0; 362 } 363 364 if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { 365 tmp_tc.c_lflag &= ~IEXTEN; 366 } 367 368 if (f&MODE_SOFT_TAB) { 369 # ifdef OXTABS 370 tmp_tc.c_oflag |= OXTABS; 371 # endif 372 # ifdef TABDLY 373 tmp_tc.c_oflag &= ~TABDLY; 374 tmp_tc.c_oflag |= TAB3; 375 # endif 376 } else { 377 # ifdef OXTABS 378 tmp_tc.c_oflag &= ~OXTABS; 379 # endif 380 # ifdef TABDLY 381 tmp_tc.c_oflag &= ~TABDLY; 382 # endif 383 } 384 385 if (f&MODE_LIT_ECHO) { 386 # ifdef ECHOCTL 387 tmp_tc.c_lflag &= ~ECHOCTL; 388 # endif 389 } else { 390 # ifdef ECHOCTL 391 tmp_tc.c_lflag |= ECHOCTL; 392 # endif 393 } 394 395 if (f == -1) { 396 onoff = 0; 397 } else { 398 if (f & MODE_INBIN) 399 tmp_tc.c_iflag &= ~ISTRIP; 400 else 401 tmp_tc.c_iflag |= ISTRIP; 402 if (f & MODE_OUTBIN) { 403 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 404 tmp_tc.c_cflag |= CS8; 405 tmp_tc.c_oflag &= ~OPOST; 406 } else { 407 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 408 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); 409 tmp_tc.c_oflag |= OPOST; 410 } 411 onoff = 1; 412 } 413 414 if (f != -1) { 415 (void) signal(SIGTSTP, susp); 416 (void) signal(SIGINFO, ayt); 417 #if defined(USE_TERMIO) && defined(NOKERNINFO) 418 tmp_tc.c_lflag |= NOKERNINFO; 419 #endif 420 /* 421 * We don't want to process ^Y here. It's just another 422 * character that we'll pass on to the back end. It has 423 * to process it because it will be processed when the 424 * user attempts to read it, not when we send it. 425 */ 426 # ifdef VDSUSP 427 tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); 428 # endif 429 /* 430 * If the VEOL character is already set, then use VEOL2, 431 * otherwise use VEOL. 432 */ 433 esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; 434 if ((tmp_tc.c_cc[VEOL] != esc) 435 # ifdef VEOL2 436 && (tmp_tc.c_cc[VEOL2] != esc) 437 # endif 438 ) { 439 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) 440 tmp_tc.c_cc[VEOL] = esc; 441 # ifdef VEOL2 442 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) 443 tmp_tc.c_cc[VEOL2] = esc; 444 # endif 445 } 446 } else { 447 (void) signal(SIGINFO, (void (*)(int)) ayt_status); 448 (void) signal(SIGTSTP, SIG_DFL); 449 (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); 450 tmp_tc = old_tc; 451 } 452 if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) 453 tcsetattr(tin, TCSANOW, &tmp_tc); 454 455 ioctl(tin, FIONBIO, (char *)&onoff); 456 ioctl(tout, FIONBIO, (char *)&onoff); 457 #if defined(TN3270) 458 if (noasynchtty == 0) { 459 ioctl(tin, FIOASYNC, (char *)&onoff); 460 } 461 #endif /* defined(TN3270) */ 462 463 } 464 465 void 466 TerminalSpeeds(long *ispeed, long *ospeed) 467 { 468 long in, out; 469 470 out = cfgetospeed(&old_tc); 471 in = cfgetispeed(&old_tc); 472 if (in == 0) 473 in = out; 474 475 *ispeed = in; 476 *ospeed = out; 477 } 478 479 int 480 TerminalWindowSize(long *rows, long *cols) 481 { 482 struct winsize ws; 483 484 if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) { 485 *rows = ws.ws_row; 486 *cols = ws.ws_col; 487 return 1; 488 } 489 return 0; 490 } 491 492 int 493 NetClose(int fd) 494 { 495 return close(fd); 496 } 497 498 499 void 500 NetNonblockingIO(int fd, int onoff) 501 { 502 ioctl(fd, FIONBIO, (char *)&onoff); 503 } 504 505 #ifdef TN3270 506 void 507 NetSigIO(int fd, int onoff) 508 { 509 ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */ 510 } 511 512 void 513 NetSetPgrp(int fd) 514 { 515 int myPid; 516 517 myPid = getpid(); 518 fcntl(fd, F_SETOWN, myPid); 519 } 520 #endif /*defined(TN3270)*/ 521 522 /* 523 * Various signal handling routines. 524 */ 525 526 /* ARGSUSED */ 527 SIG_FUNC_RET 528 intr(int sig) 529 { 530 if (localchars) { 531 intp(); 532 return; 533 } 534 setcommandmode(); 535 longjmp(toplevel, -1); 536 } 537 538 /* ARGSUSED */ 539 SIG_FUNC_RET 540 intr2(int sig) 541 { 542 if (localchars) { 543 #ifdef KLUDGELINEMODE 544 if (kludgelinemode) 545 sendbrk(); 546 else 547 #endif 548 sendabort(); 549 return; 550 } 551 } 552 553 /* ARGSUSED */ 554 SIG_FUNC_RET 555 susp(int sig) 556 { 557 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) 558 return; 559 if (localchars) 560 sendsusp(); 561 } 562 563 /* ARGSUSED */ 564 SIG_FUNC_RET 565 sendwin(int sig) 566 { 567 if (connected) { 568 sendnaws(); 569 } 570 } 571 572 /* ARGSUSED */ 573 SIG_FUNC_RET 574 ayt(int sig) 575 { 576 if (connected) 577 sendayt(); 578 else 579 ayt_status(); 580 } 581 582 583 void 584 sys_telnet_init(void) 585 { 586 (void) signal(SIGINT, intr); 587 (void) signal(SIGQUIT, intr2); 588 (void) signal(SIGPIPE, SIG_IGN); 589 (void) signal(SIGWINCH, sendwin); 590 (void) signal(SIGTSTP, susp); 591 (void) signal(SIGINFO, ayt); 592 593 setconnmode(0); 594 595 NetNonblockingIO(net, 1); 596 597 #ifdef TN3270 598 if (noasynchnet == 0) { /* DBX can't handle! */ 599 NetSigIO(net, 1); 600 NetSetPgrp(net); 601 } 602 #endif /* defined(TN3270) */ 603 604 if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) { 605 perror("SetSockOpt"); 606 } 607 } 608 609 /* 610 * Process rings - 611 * 612 * This routine tries to fill up/empty our various rings. 613 * 614 * The parameter specifies whether this is a poll operation, 615 * or a block-until-something-happens operation. 616 * 617 * The return value is 1 if something happened, 0 if not, < 0 if an 618 * error occurred. 619 */ 620 621 int 622 process_rings(int netin, int netout, int netex, int ttyin, int ttyout, 623 int dopoll) /* If 0, then block until something to do */ 624 { 625 struct pollfd set[3]; 626 int c; 627 /* One wants to be a bit careful about setting returnValue 628 * to one, since a one implies we did some useful work, 629 * and therefore probably won't be called to block next 630 * time (TN3270 mode only). 631 */ 632 int returnValue = 0; 633 634 set[0].fd = net; 635 set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) | 636 (netex ? POLLPRI : 0); 637 set[1].fd = tout; 638 set[1].events = ttyout ? POLLOUT : 0; 639 set[2].fd = tin; 640 set[2].events = ttyin ? POLLIN : 0; 641 642 if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) { 643 if (c == -1) { 644 /* 645 * we can get EINTR if we are in line mode, 646 * and the user does an escape (TSTP), or 647 * some other signal generator. 648 */ 649 if (errno == EINTR) { 650 return 0; 651 } 652 #ifdef TN3270 653 /* 654 * we can get EBADF if we were in transparent 655 * mode, and the transcom process died. 656 */ 657 if (errno == EBADF) 658 return 0; 659 #endif /* defined(TN3270) */ 660 /* I don't like this, does it ever happen? */ 661 printf("sleep(5) from telnet, after poll\r\n"); 662 sleep(5); 663 } 664 return 0; 665 } 666 667 /* 668 * Any urgent data? 669 */ 670 if (set[0].revents & POLLPRI) { 671 SYNCHing = 1; 672 (void) ttyflush(1); /* flush already enqueued data */ 673 } 674 675 /* 676 * Something to read from the network... 677 */ 678 if (set[0].revents & POLLIN) { 679 int canread; 680 681 canread = ring_empty_consecutive(&netiring); 682 c = recv(net, (char *)netiring.supply, canread, 0); 683 if (c < 0 && errno == EWOULDBLOCK) { 684 c = 0; 685 } else if (c <= 0) { 686 return -1; 687 } 688 if (netdata) { 689 Dump('<', netiring.supply, c); 690 } 691 if (c) 692 ring_supplied(&netiring, c); 693 returnValue = 1; 694 } 695 696 /* 697 * Something to read from the tty... 698 */ 699 if (set[2].revents & POLLIN) { 700 c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring)); 701 if (c < 0 && errno == EIO) 702 c = 0; 703 if (c < 0 && errno == EWOULDBLOCK) { 704 c = 0; 705 } else { 706 if (c < 0) { 707 return -1; 708 } 709 if (c == 0) { 710 /* must be an EOF... */ 711 if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { 712 *ttyiring.supply = termEofChar; 713 c = 1; 714 } else { 715 clienteof = 1; 716 shutdown(net, 1); 717 return 0; 718 } 719 } 720 if (termdata) { 721 Dump('<', ttyiring.supply, c); 722 } 723 ring_supplied(&ttyiring, c); 724 } 725 returnValue = 1; /* did something useful */ 726 } 727 728 if (set[0].revents & POLLOUT) { 729 returnValue |= netflush(); 730 } 731 732 if (set[1].revents & (POLLHUP|POLLNVAL)) 733 return(-1); 734 735 if (set[1].revents & POLLOUT) { 736 returnValue |= (ttyflush(SYNCHing|flushout) > 0); 737 } 738 739 return returnValue; 740 } 741