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