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