diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c new file mode 100644 index 0000000000..541b7c5d07 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c @@ -0,0 +1,886 @@ +/* + * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)sys_bsd.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * The following routines try to encapsulate what is system dependent + * (at least between 4.x and dos) which is used in telnet.c. + */ + + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <signal.h> +#include <errno.h> +#include <arpa/telnet.h> + +#include "ring.h" + +#include "defines.h" +#include "externs.h" +#include "types.h" + +#define SIG_FUNC_RET void + +int tout; /* Output file descriptor */ +static int tin; /* Input file descriptor */ +int net = -1; + + +#ifndef USE_TERMIO +struct tchars otc = { 0 }, ntc = { 0 }; +struct ltchars oltc = { 0 }, nltc = { 0 }; +struct sgttyb ottyb = { 0 }, nttyb = { 0 }; +int olmode = 0; +#define cfgetispeed(ptr) (ptr)->sg_ispeed +#define cfgetospeed(ptr) (ptr)->sg_ospeed +#define old_tc ottyb + +#else /* USE_TERMIO */ +static struct termio old_tc = { 0 }; +extern struct termio new_tc; +#endif /* USE_TERMIO */ + +static fd_set ibits, obits, xbits; + +static SIG_FUNC_RET susp(int); +void fatal_tty_error(char *doing_what); + + +void +init_sys() +{ + tout = fileno(stdout); + tin = fileno(stdin); + FD_ZERO(&ibits); + FD_ZERO(&obits); + FD_ZERO(&xbits); + + errno = 0; +} + + +int +TerminalWrite(buf, n) + char *buf; + int n; +{ + return (write(tout, buf, n)); +} + +static int +TerminalRead(buf, n) + char *buf; + int n; +{ + return (read(tin, buf, n)); +} + +#ifdef KLUDGELINEMODE +extern int kludgelinemode; +#endif +/* + * TerminalSpecialChars() + * + * Look at an input character to see if it is a special character + * and decide what to do. + * + * Output: + * + * 0 Don't add this character. + * 1 Do add this character + */ +int +TerminalSpecialChars(c) + int c; +{ + /* + * Don't check for signal characters here. If MODE_TRAPSIG is on, + * then the various signal handlers will catch the characters. If + * the character in question gets here, then it must have been LNEXTed + */ + if (c == termQuitChar) { +#ifdef KLUDGELINEMODE + if (kludgelinemode) { + if (sendbrk() == -1) { + /* This won't return. */ + fatal_tty_error("write"); + } + return (0); + } +#endif + } else if (c == termFlushChar) { + /* Transmit Abort Output */ + if (xmitAO() == -1) { + /* This won't return. */ + fatal_tty_error("write"); + } + return (0); + } else if (!MODE_LOCAL_CHARS(globalmode)) { + if (c == termKillChar) { + xmitEL(); + return (0); + } else if (c == termEraseChar) { + xmitEC(); /* Transmit Erase Character */ + return (0); + } + } + return (1); +} + + +/* + * Flush output to the terminal + */ + +void +TerminalFlushOutput() +{ + if (isatty(fileno(stdout))) { + (void) ioctl(fileno(stdout), TIOCFLUSH, NULL); + } +} + +void +TerminalSaveState() +{ +#ifndef USE_TERMIO + (void) ioctl(0, TIOCGETP, &ottyb); + (void) ioctl(0, TIOCGETC, &otc); + (void) ioctl(0, TIOCGLTC, &oltc); + (void) ioctl(0, TIOCLGET, &olmode); + + ntc = otc; + nltc = oltc; + nttyb = ottyb; + +#else /* USE_TERMIO */ + (void) tcgetattr(0, &old_tc); + + new_tc = old_tc; + termAytChar = CONTROL('T'); +#endif /* USE_TERMIO */ +} + +cc_t * +tcval(func) + register int func; +{ + switch (func) { + case SLC_IP: return (&termIntChar); + case SLC_ABORT: return (&termQuitChar); + case SLC_EOF: return (&termEofChar); + case SLC_EC: return (&termEraseChar); + case SLC_EL: return (&termKillChar); + case SLC_XON: return (&termStartChar); + case SLC_XOFF: return (&termStopChar); + case SLC_FORW1: return (&termForw1Char); +#ifdef USE_TERMIO + case SLC_FORW2: return (&termForw2Char); + case SLC_AO: return (&termFlushChar); + case SLC_SUSP: return (&termSuspChar); + case SLC_EW: return (&termWerasChar); + case SLC_RP: return (&termRprntChar); + case SLC_LNEXT: return (&termLiteralNextChar); +#endif + + case SLC_SYNCH: + case SLC_BRK: + case SLC_EOR: + default: + return ((cc_t *)0); + } +} + +void +TerminalDefaultChars() +{ +#ifndef USE_TERMIO + ntc = otc; + nltc = oltc; + nttyb.sg_kill = ottyb.sg_kill; + nttyb.sg_erase = ottyb.sg_erase; +#else /* USE_TERMIO */ + (void) memcpy(new_tc.c_cc, old_tc.c_cc, sizeof (old_tc.c_cc)); + termAytChar = CONTROL('T'); +#endif /* USE_TERMIO */ +} + +/* + * TerminalNewMode - set up terminal to a specific mode. + * MODE_ECHO: do local terminal echo + * MODE_FLOW: do local flow control + * MODE_TRAPSIG: do local mapping to TELNET IAC sequences + * MODE_EDIT: do local line editing + * + * Command mode: + * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG + * local echo + * local editing + * local xon/xoff + * local signal mapping + * + * Linemode: + * local/no editing + * Both Linemode and Single Character mode: + * local/remote echo + * local/no xon/xoff + * local/no signal mapping + */ + + +void +TerminalNewMode(f) + register int f; +{ + static int prevmode = -2; /* guaranteed unique */ +#ifndef USE_TERMIO + struct tchars tc; + struct ltchars ltc; + struct sgttyb sb; + int lmode; +#else /* USE_TERMIO */ + struct termio tmp_tc; +#endif /* USE_TERMIO */ + int onoff; + int old; + cc_t esc; + sigset_t nset; + + globalmode = f&~MODE_FORCE; + if (prevmode == f) + return; + + /* + * Write any outstanding data before switching modes + * ttyflush() returns 0 only when there was no data + * to write out; it returns -1 if it couldn't do + * anything at all, returns -2 if there was a write + * error (other than EWOULDBLOCK), and otherwise it + * returns 1 + the number of characters left to write. + */ +#ifndef USE_TERMIO + /* + * We would really like ask the kernel to wait for the output + * to drain, like we can do with the TCSADRAIN, but we don't have + * that option. The only ioctl that waits for the output to + * drain, TIOCSETP, also flushes the input queue, which is NOT + * what we want(TIOCSETP is like TCSADFLUSH). + */ +#endif + old = ttyflush(SYNCHing|flushout); + if (old == -1 || old > 1) { +#ifdef USE_TERMIO + (void) tcgetattr(tin, &tmp_tc); +#endif /* USE_TERMIO */ + do { + /* + * Wait for data to drain, then flush again. + */ +#ifdef USE_TERMIO + (void) tcsetattr(tin, TCSADRAIN, &tmp_tc); +#endif /* USE_TERMIO */ + old = ttyflush(SYNCHing|flushout); + } while (old == -1 || old > 1); + } + + old = prevmode; + prevmode = f&~MODE_FORCE; +#ifndef USE_TERMIO + sb = nttyb; + tc = ntc; + ltc = nltc; + lmode = olmode; +#else + tmp_tc = new_tc; +#endif + + if (f&MODE_ECHO) { +#ifndef USE_TERMIO + sb.sg_flags |= ECHO; +#else + tmp_tc.c_lflag |= ECHO; + tmp_tc.c_oflag |= ONLCR; + if (crlf) + tmp_tc.c_iflag |= ICRNL; +#endif + } else { +#ifndef USE_TERMIO + sb.sg_flags &= ~ECHO; +#else + tmp_tc.c_lflag &= ~ECHO; + tmp_tc.c_oflag &= ~ONLCR; +#ifdef notdef + if (crlf) + tmp_tc.c_iflag &= ~ICRNL; +#endif +#endif + } + + if ((f&MODE_FLOW) == 0) { +#ifndef USE_TERMIO + tc.t_startc = _POSIX_VDISABLE; + tc.t_stopc = _POSIX_VDISABLE; +#else + tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ + } else { + if (restartany < 0) { + /* Leave the IXANY bit alone */ + tmp_tc.c_iflag |= IXOFF|IXON; + } else if (restartany > 0) { + tmp_tc.c_iflag |= IXOFF|IXON|IXANY; + } else { + tmp_tc.c_iflag |= IXOFF|IXON; + tmp_tc.c_iflag &= ~IXANY; + } +#endif + } + + if ((f&MODE_TRAPSIG) == 0) { +#ifndef USE_TERMIO + tc.t_intrc = _POSIX_VDISABLE; + tc.t_quitc = _POSIX_VDISABLE; + tc.t_eofc = _POSIX_VDISABLE; + ltc.t_suspc = _POSIX_VDISABLE; + ltc.t_dsuspc = _POSIX_VDISABLE; +#else + tmp_tc.c_lflag &= ~ISIG; +#endif + localchars = 0; + } else { +#ifdef USE_TERMIO + tmp_tc.c_lflag |= ISIG; +#endif + localchars = 1; + } + + if (f&MODE_EDIT) { +#ifndef USE_TERMIO + sb.sg_flags &= ~CBREAK; + sb.sg_flags |= CRMOD; +#else + tmp_tc.c_lflag |= ICANON; +#endif + } else { +#ifndef USE_TERMIO + sb.sg_flags |= CBREAK; + if (f&MODE_ECHO) + sb.sg_flags |= CRMOD; + else + sb.sg_flags &= ~CRMOD; +#else + tmp_tc.c_lflag &= ~ICANON; + tmp_tc.c_iflag &= ~ICRNL; + tmp_tc.c_cc[VMIN] = 1; + tmp_tc.c_cc[VTIME] = 0; +#endif + } + + if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { +#ifndef USE_TERMIO + ltc.t_lnextc = _POSIX_VDISABLE; +#else + tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE); +#endif + } + + if (f&MODE_SOFT_TAB) { +#ifndef USE_TERMIO + sb.sg_flags |= XTABS; +#else + tmp_tc.c_oflag &= ~TABDLY; + tmp_tc.c_oflag |= TAB3; +#endif + } else { +#ifndef USE_TERMIO + sb.sg_flags &= ~XTABS; +#else + tmp_tc.c_oflag &= ~TABDLY; +#endif + } + + if (f&MODE_LIT_ECHO) { +#ifndef USE_TERMIO + lmode &= ~LCTLECH; +#else + tmp_tc.c_lflag &= ~ECHOCTL; +#endif + } else { +#ifndef USE_TERMIO + lmode |= LCTLECH; +#else + tmp_tc.c_lflag |= ECHOCTL; +#endif + } + + if (f == -1) { + onoff = 0; + } else { +#ifndef USE_TERMIO + if (f & MODE_OUTBIN) + lmode |= LLITOUT; + else + lmode &= ~LLITOUT; +#else + if (f & MODE_OUTBIN) { + tmp_tc.c_cflag &= ~(CSIZE|PARENB); + tmp_tc.c_cflag |= CS8; + tmp_tc.c_oflag &= ~OPOST; + } else { + tmp_tc.c_cflag &= ~(CSIZE|PARENB); + tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); + tmp_tc.c_oflag |= OPOST; + } +#endif + onoff = 1; + } + + if (f != -1) { + + (void) signal(SIGTSTP, susp); + +#if defined(USE_TERMIO) && defined(NOKERNINFO) + tmp_tc.c_lflag |= NOKERNINFO; +#endif + /* + * We don't want to process ^Y here. It's just another + * character that we'll pass on to the back end. It has + * to process it because it will be processed when the + * user attempts to read it, not when we send it. + */ +#ifndef USE_TERMIO + ltc.t_dsuspc = _POSIX_VDISABLE; +#else + tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); +#endif +#ifdef USE_TERMIO + /* + * If the VEOL character is already set, then use VEOL2, + * otherwise use VEOL. + */ + esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; + if ((tmp_tc.c_cc[VEOL] != esc) + /* XXX */ && + (tmp_tc.c_cc[VEOL2] != esc) + /* XXX */) { + if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) + tmp_tc.c_cc[VEOL] = esc; + else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) + tmp_tc.c_cc[VEOL2] = esc; + } +#else + if (tc.t_brkc == (cc_t)(_POSIX_VDISABLE)) + tc.t_brkc = esc; +#endif + } else { + (void) signal(SIGTSTP, SIG_DFL); + (void) sigemptyset(&nset); + (void) sigaddset(&nset, SIGTSTP); + (void) sigprocmask(SIG_UNBLOCK, &nset, 0); +#ifndef USE_TERMIO + ltc = oltc; + tc = otc; + sb = ottyb; + lmode = olmode; +#else + tmp_tc = old_tc; +#endif + } + if (isatty(tin)) { +#ifndef USE_TERMIO + (void) ioctl(tin, TIOCLSET, &lmode); + (void) ioctl(tin, TIOCSLTC, <c); + (void) ioctl(tin, TIOCSETC, &tc); + (void) ioctl(tin, TIOCSETN, &sb); +#else + if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) + (void) tcsetattr(tin, TCSANOW, &tmp_tc); +#endif + (void) ioctl(tin, FIONBIO, &onoff); + (void) ioctl(tout, FIONBIO, &onoff); + } + +} + +/* + * This code assumes that the values B0, B50, B75... + * are in ascending order. They do not have to be + * contiguous. + */ +static struct termspeeds { + int speed; + int value; +} termspeeds[] = { + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, + { 38400, B38400 }, { 57600, B57600 }, { 76800, B76800 }, + { 115200, B115200 }, { 153600, B153600 }, { 230400, B230400 }, + { 307200, B307200 }, { 460800, B460800 }, { -1, B0 } +}; + +void +TerminalSpeeds(ispeed, ospeed) + int *ispeed; + int *ospeed; +{ + register struct termspeeds *tp; + register int in, out; + + out = cfgetospeed(&old_tc); + in = cfgetispeed(&old_tc); + if (in == 0) + in = out; + + tp = termspeeds; + while ((tp->speed != -1) && (tp->value < in)) { + tp++; + } + if (tp->speed == -1) + tp--; /* back up to fastest defined speed */ + *ispeed = tp->speed; + + tp = termspeeds; + while ((tp->speed != -1) && (tp->value < out)) { + tp++; + } + if (tp->speed == -1) + tp--; + *ospeed = tp->speed; +} + +int +TerminalWindowSize(rows, cols) + unsigned short *rows, *cols; +{ + struct winsize ws; + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { + *rows = ws.ws_row; + *cols = ws.ws_col; + return (1); + } + return (0); +} + +static void +NetNonblockingIO(fd, onoff) + int fd; + int onoff; +{ + (void) ioctl(fd, FIONBIO, &onoff); +} + +/* + * Various signal handling routines. + */ + +/* ARGSUSED */ +static SIG_FUNC_RET +deadpeer(sig) + int sig; +{ + /* + * Once is all we should catch SIGPIPE. If we get it again, + * it means we tried to put still more data out to a pipe + * which has disappeared. In that case, telnet will exit. + */ + (void) signal(SIGPIPE, SIG_IGN); + flushout = 1; + setcommandmode(); + longjmp(peerdied, -1); +} + +boolean_t intr_happened = B_FALSE; +boolean_t intr_waiting = B_FALSE; + +/* ARGSUSED */ +static SIG_FUNC_RET +intr(sig) + int sig; +{ + if (intr_waiting) { + intr_happened = 1; + return; + } + (void) signal(SIGINT, intr); + if (localchars) { + intp(); + return; + } + setcommandmode(); + longjmp(toplevel, -1); +} + +/* ARGSUSED */ +static SIG_FUNC_RET +intr2(sig) + int sig; +{ + (void) signal(SIGQUIT, intr2); + if (localchars) { + /* + * Ignore return to the next two function calls + * since we're doing SIGQUIT + */ +#ifdef KLUDGELINEMODE + if (kludgelinemode) { + (void) sendbrk(); + } + else +#endif + sendabort(); + return; + } +} + +/* ARGSUSED */ +static SIG_FUNC_RET +susp(sig) + int sig; +{ + (void) signal(SIGTSTP, susp); + if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) + return; + if (localchars) + sendsusp(); +} + +/* ARGSUSED */ +static SIG_FUNC_RET +sendwin(sig) + int sig; +{ + (void) signal(SIGWINCH, sendwin); + if (connected) { + sendnaws(); + } +} + +void +sys_telnet_init() +{ + (void) signal(SIGINT, intr); + (void) signal(SIGQUIT, intr2); + (void) signal(SIGPIPE, deadpeer); + (void) signal(SIGWINCH, sendwin); + (void) signal(SIGTSTP, susp); + + setconnmode(0); + + NetNonblockingIO(net, 1); + + if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) { + perror("SetSockOpt"); + } +} + + +/* + * fatal_tty_error - + * Handle case where there is an unrecoverable error on the tty + * connections. Print an error, reset the terminal settings + * and get out as painlessly as possible. + */ +void +fatal_tty_error(char *doing_what) +{ + TerminalNewMode(-1); + (void) fprintf(stderr, "Error processing %s: %s\n", doing_what, + strerror(errno)); + exit(1); +} + + +/* + * Process rings - + * + * This routine tries to fill up/empty our various rings. + * + * The parameter specifies whether this is a poll operation, + * or a block-until-something-happens operation. + * + * The return value is 1 if something happened, 0 if not. + */ + +int +process_rings(netin, netout, netex, ttyin, ttyout, poll) + int poll; /* If 0, then block until something to do */ +{ + register int c; + /* + * One wants to be a bit careful about setting returnValue + * to one, since a one implies we did some useful work, + * and therefore probably won't be called to block next + * time (TN3270 mode only). + */ + int returnValue = 0; + static struct timeval TimeValue = { 0 }; + int i; + + if (netout) { + FD_SET(net, &obits); + } + if (ttyout) { + FD_SET(tout, &obits); + } + if (ttyin) { + FD_SET(tin, &ibits); + } + if (netin) { + FD_SET(net, &ibits); + } + if (netex) { + FD_SET(net, &xbits); + } + if ((c = select(16, &ibits, &obits, &xbits, + (poll == 0) ? NULL : &TimeValue)) < 0) { + if (c == -1) { + /* + * we can get EINTR if we are in line mode, + * and the user does an escape (TSTP), or + * some other signal generator. + */ + if (errno == EINTR) { + return (0); + } + /* I don't like this, does it ever happen? */ + (void) printf("sleep(5) from telnet, after select\r\n"); + (void) sleep(5); + } + return (0); + } + + /* + * Any urgent data? + */ + if (FD_ISSET(net, &xbits)) { + FD_CLR(net, &xbits); + SYNCHing = 1; + + /* flush any data that is already enqueued */ + i = ttyflush(1); + if (i == -2) { + /* This will not return. */ + fatal_tty_error("write"); + } + } + + /* + * Something to read from the network... + */ + if (FD_ISSET(net, &ibits)) { + int canread; + + FD_CLR(net, &ibits); + canread = ring_empty_consecutive(&netiring); + c = recv(net, netiring.supply, canread, 0); + if (c < 0 && errno == EWOULDBLOCK) { + c = 0; + } else if (c <= 0) { + return (-1); + } + if (netdata) { + Dump('<', netiring.supply, c); + } + if (c) + ring_supplied(&netiring, c); + returnValue = 1; + } + + /* + * Something to read from the tty... + */ + if (FD_ISSET(tin, &ibits)) { + FD_CLR(tin, &ibits); + c = TerminalRead((char *)ttyiring.supply, + ring_empty_consecutive(&ttyiring)); + if (c < 0) { + if (errno != EWOULDBLOCK) { + /* This will not return. */ + fatal_tty_error("read"); + } + c = 0; + } else { + /* EOF detection for line mode!!!! */ + if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && + isatty(tin)) { + /* must be an EOF... */ + eof_pending = 1; + return (1); + } + if (c <= 0) { + returnValue = -1; + goto next; + } + if (termdata) { + Dump('<', ttyiring.supply, c); + } + ring_supplied(&ttyiring, c); + } + returnValue = 1; /* did something useful */ + } + +next: + if (FD_ISSET(net, &obits)) { + FD_CLR(net, &obits); + returnValue |= netflush(); + } + if (FD_ISSET(tout, &obits)) { + FD_CLR(tout, &obits); + i = ttyflush(SYNCHing|flushout); + if (i == -2) { + /* This will not return. */ + fatal_tty_error("write"); + } + returnValue |= (i > 0); + } + + return (returnValue); +} |