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