1 /* $NetBSD: sys_bsd.c,v 1.29 2003/08/07 11:16:10 agc 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.29 2003/08/07 11:16:10 agc 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 if (old == -2) 314 return; 315 } while (old < 0 || old > 1); 316 } 317 318 old = prevmode; 319 prevmode = f&~MODE_FORCE; 320 tmp_tc = new_tc; 321 322 if (f&MODE_ECHO) { 323 tmp_tc.c_lflag |= ECHO; 324 tmp_tc.c_oflag |= ONLCR; 325 if (crlf) 326 tmp_tc.c_iflag |= ICRNL; 327 } else { 328 tmp_tc.c_lflag &= ~ECHO; 329 tmp_tc.c_oflag &= ~ONLCR; 330 # ifdef notdef 331 if (crlf) 332 tmp_tc.c_iflag &= ~ICRNL; 333 # endif 334 } 335 336 if ((f&MODE_FLOW) == 0) { 337 tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ 338 } else { 339 if (restartany < 0) { 340 tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */ 341 } else if (restartany > 0) { 342 tmp_tc.c_iflag |= IXOFF|IXON|IXANY; 343 } else { 344 tmp_tc.c_iflag |= IXOFF|IXON; 345 tmp_tc.c_iflag &= ~IXANY; 346 } 347 } 348 349 if ((f&MODE_TRAPSIG) == 0) { 350 tmp_tc.c_lflag &= ~ISIG; 351 localchars = 0; 352 } else { 353 tmp_tc.c_lflag |= ISIG; 354 localchars = 1; 355 } 356 357 if (f&MODE_EDIT) { 358 tmp_tc.c_lflag |= ICANON; 359 } else { 360 tmp_tc.c_lflag &= ~ICANON; 361 tmp_tc.c_iflag &= ~ICRNL; 362 tmp_tc.c_cc[VMIN] = 1; 363 tmp_tc.c_cc[VTIME] = 0; 364 } 365 366 if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { 367 tmp_tc.c_lflag &= ~IEXTEN; 368 } 369 370 if (f&MODE_SOFT_TAB) { 371 # ifdef OXTABS 372 tmp_tc.c_oflag |= OXTABS; 373 # endif 374 # ifdef TABDLY 375 tmp_tc.c_oflag &= ~TABDLY; 376 tmp_tc.c_oflag |= TAB3; 377 # endif 378 } else { 379 # ifdef OXTABS 380 tmp_tc.c_oflag &= ~OXTABS; 381 # endif 382 # ifdef TABDLY 383 tmp_tc.c_oflag &= ~TABDLY; 384 # endif 385 } 386 387 if (f&MODE_LIT_ECHO) { 388 # ifdef ECHOCTL 389 tmp_tc.c_lflag &= ~ECHOCTL; 390 # endif 391 } else { 392 # ifdef ECHOCTL 393 tmp_tc.c_lflag |= ECHOCTL; 394 # endif 395 } 396 397 if (f == -1) { 398 onoff = 0; 399 } else { 400 if (f & MODE_INBIN) 401 tmp_tc.c_iflag &= ~ISTRIP; 402 else 403 tmp_tc.c_iflag |= ISTRIP; 404 if (f & MODE_OUTBIN) { 405 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 406 tmp_tc.c_cflag |= CS8; 407 tmp_tc.c_oflag &= ~OPOST; 408 } else { 409 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 410 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); 411 tmp_tc.c_oflag |= OPOST; 412 } 413 onoff = 1; 414 } 415 416 if (f != -1) { 417 (void) signal(SIGTSTP, susp); 418 (void) signal(SIGINFO, ayt); 419 #if defined(USE_TERMIO) && defined(NOKERNINFO) 420 tmp_tc.c_lflag |= NOKERNINFO; 421 #endif 422 /* 423 * We don't want to process ^Y here. It's just another 424 * character that we'll pass on to the back end. It has 425 * to process it because it will be processed when the 426 * user attempts to read it, not when we send it. 427 */ 428 # ifdef VDSUSP 429 tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); 430 # endif 431 /* 432 * If the VEOL character is already set, then use VEOL2, 433 * otherwise use VEOL. 434 */ 435 esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; 436 if ((tmp_tc.c_cc[VEOL] != esc) 437 # ifdef VEOL2 438 && (tmp_tc.c_cc[VEOL2] != esc) 439 # endif 440 ) { 441 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) 442 tmp_tc.c_cc[VEOL] = esc; 443 # ifdef VEOL2 444 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) 445 tmp_tc.c_cc[VEOL2] = esc; 446 # endif 447 } 448 } else { 449 (void) signal(SIGINFO, (void (*)(int)) ayt_status); 450 (void) signal(SIGTSTP, SIG_DFL); 451 (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); 452 tmp_tc = old_tc; 453 } 454 if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) 455 tcsetattr(tin, TCSANOW, &tmp_tc); 456 457 ioctl(tin, FIONBIO, (char *)&onoff); 458 ioctl(tout, FIONBIO, (char *)&onoff); 459 #if defined(TN3270) 460 if (noasynchtty == 0) { 461 ioctl(tin, FIOASYNC, (char *)&onoff); 462 } 463 #endif /* defined(TN3270) */ 464 465 } 466 467 void 468 TerminalSpeeds(long *ispeed, long *ospeed) 469 { 470 long in, out; 471 472 out = cfgetospeed(&old_tc); 473 in = cfgetispeed(&old_tc); 474 if (in == 0) 475 in = out; 476 477 *ispeed = in; 478 *ospeed = out; 479 } 480 481 int 482 TerminalWindowSize(long *rows, long *cols) 483 { 484 struct winsize ws; 485 486 if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) { 487 *rows = ws.ws_row; 488 *cols = ws.ws_col; 489 return 1; 490 } 491 return 0; 492 } 493 494 int 495 NetClose(int fd) 496 { 497 return close(fd); 498 } 499 500 501 void 502 NetNonblockingIO(int fd, int onoff) 503 { 504 ioctl(fd, FIONBIO, (char *)&onoff); 505 } 506 507 #ifdef TN3270 508 void 509 NetSigIO(int fd, int onoff) 510 { 511 ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */ 512 } 513 514 void 515 NetSetPgrp(int fd) 516 { 517 int myPid; 518 519 myPid = getpid(); 520 fcntl(fd, F_SETOWN, myPid); 521 } 522 #endif /*defined(TN3270)*/ 523 524 /* 525 * Various signal handling routines. 526 */ 527 528 /* ARGSUSED */ 529 SIG_FUNC_RET 530 deadpeer(int sig) 531 { 532 setcommandmode(); 533 longjmp(peerdied, -1); 534 } 535 536 /* ARGSUSED */ 537 SIG_FUNC_RET 538 intr(int sig) 539 { 540 if (localchars) { 541 intp(); 542 return; 543 } 544 setcommandmode(); 545 longjmp(toplevel, -1); 546 } 547 548 /* ARGSUSED */ 549 SIG_FUNC_RET 550 intr2(int sig) 551 { 552 if (localchars) { 553 #ifdef KLUDGELINEMODE 554 if (kludgelinemode) 555 sendbrk(); 556 else 557 #endif 558 sendabort(); 559 return; 560 } 561 } 562 563 /* ARGSUSED */ 564 SIG_FUNC_RET 565 susp(int sig) 566 { 567 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) 568 return; 569 if (localchars) 570 sendsusp(); 571 } 572 573 /* ARGSUSED */ 574 SIG_FUNC_RET 575 sendwin(int sig) 576 { 577 if (connected) { 578 sendnaws(); 579 } 580 } 581 582 /* ARGSUSED */ 583 SIG_FUNC_RET 584 ayt(int sig) 585 { 586 if (connected) 587 sendayt(); 588 else 589 ayt_status(); 590 } 591 592 593 void 594 sys_telnet_init(void) 595 { 596 (void) signal(SIGINT, intr); 597 (void) signal(SIGQUIT, intr2); 598 (void) signal(SIGPIPE, deadpeer); 599 (void) signal(SIGWINCH, sendwin); 600 (void) signal(SIGTSTP, susp); 601 (void) signal(SIGINFO, ayt); 602 603 setconnmode(0); 604 605 NetNonblockingIO(net, 1); 606 607 #ifdef TN3270 608 if (noasynchnet == 0) { /* DBX can't handle! */ 609 NetSigIO(net, 1); 610 NetSetPgrp(net); 611 } 612 #endif /* defined(TN3270) */ 613 614 if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) { 615 perror("SetSockOpt"); 616 } 617 } 618 619 /* 620 * Process rings - 621 * 622 * This routine tries to fill up/empty our various rings. 623 * 624 * The parameter specifies whether this is a poll operation, 625 * or a block-until-something-happens operation. 626 * 627 * The return value is 1 if something happened, 0 if not, < 0 if an 628 * error occured. 629 */ 630 631 int 632 process_rings(int netin, int netout, int netex, int ttyin, int ttyout, 633 int dopoll) /* If 0, then block until something to do */ 634 { 635 struct pollfd set[3]; 636 int c; 637 /* One wants to be a bit careful about setting returnValue 638 * to one, since a one implies we did some useful work, 639 * and therefore probably won't be called to block next 640 * time (TN3270 mode only). 641 */ 642 int returnValue = 0; 643 644 set[0].fd = net; 645 set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) | 646 (netex ? POLLPRI : 0); 647 set[1].fd = tout; 648 set[1].events = ttyout ? POLLOUT : 0; 649 set[2].fd = tin; 650 set[2].events = ttyin ? POLLIN : 0; 651 652 if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) { 653 if (c == -1) { 654 /* 655 * we can get EINTR if we are in line mode, 656 * and the user does an escape (TSTP), or 657 * some other signal generator. 658 */ 659 if (errno == EINTR) { 660 return 0; 661 } 662 #ifdef TN3270 663 /* 664 * we can get EBADF if we were in transparent 665 * mode, and the transcom process died. 666 */ 667 if (errno == EBADF) 668 return 0; 669 #endif /* defined(TN3270) */ 670 /* I don't like this, does it ever happen? */ 671 printf("sleep(5) from telnet, after poll\r\n"); 672 sleep(5); 673 } 674 return 0; 675 } 676 677 /* 678 * Any urgent data? 679 */ 680 if (set[0].revents & POLLPRI) { 681 SYNCHing = 1; 682 (void) ttyflush(1); /* flush already enqueued data */ 683 } 684 685 /* 686 * Something to read from the network... 687 */ 688 if (set[0].revents & POLLIN) { 689 int canread; 690 691 canread = ring_empty_consecutive(&netiring); 692 c = recv(net, (char *)netiring.supply, canread, 0); 693 if (c < 0 && errno == EWOULDBLOCK) { 694 c = 0; 695 } else if (c <= 0) { 696 return -1; 697 } 698 if (netdata) { 699 Dump('<', netiring.supply, c); 700 } 701 if (c) 702 ring_supplied(&netiring, c); 703 returnValue = 1; 704 } 705 706 /* 707 * Something to read from the tty... 708 */ 709 if (set[2].revents & POLLIN) { 710 c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring)); 711 if (c < 0 && errno == EIO) 712 c = 0; 713 if (c < 0 && errno == EWOULDBLOCK) { 714 c = 0; 715 } else { 716 if (c < 0) { 717 return -1; 718 } 719 if (c == 0) { 720 /* must be an EOF... */ 721 if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { 722 *ttyiring.supply = termEofChar; 723 c = 1; 724 } else { 725 clienteof = 1; 726 shutdown(net, 1); 727 return 0; 728 } 729 } 730 if (termdata) { 731 Dump('<', ttyiring.supply, c); 732 } 733 ring_supplied(&ttyiring, c); 734 } 735 returnValue = 1; /* did something useful */ 736 } 737 738 if (set[0].revents & POLLOUT) { 739 returnValue |= netflush(); 740 } 741 if (set[1].revents & POLLOUT) { 742 returnValue |= (ttyflush(SYNCHing|flushout) > 0); 743 } 744 745 return returnValue; 746 } 747