Home | History | Annotate | Line # | Download | only in telnet
sys_bsd.c revision 1.30
      1 /*	$NetBSD: sys_bsd.c,v 1.30 2004/02/24 15:12:53 wiz 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.30 2004/02/24 15:12:53 wiz 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 /*
    526  * Various signal handling routines.
    527  */
    528 
    529 /* ARGSUSED */
    530 SIG_FUNC_RET
    531 deadpeer(int sig)
    532 {
    533 	setcommandmode();
    534 	longjmp(peerdied, -1);
    535 }
    536 
    537 /* ARGSUSED */
    538 SIG_FUNC_RET
    539 intr(int sig)
    540 {
    541     if (localchars) {
    542 	intp();
    543 	return;
    544     }
    545     setcommandmode();
    546     longjmp(toplevel, -1);
    547 }
    548 
    549 /* ARGSUSED */
    550 SIG_FUNC_RET
    551 intr2(int sig)
    552 {
    553     if (localchars) {
    554 #ifdef	KLUDGELINEMODE
    555 	if (kludgelinemode)
    556 	    sendbrk();
    557 	else
    558 #endif
    559 	    sendabort();
    560 	return;
    561     }
    562 }
    563 
    564 /* ARGSUSED */
    565 SIG_FUNC_RET
    566 susp(int sig)
    567 {
    568     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
    569 	return;
    570     if (localchars)
    571 	sendsusp();
    572 }
    573 
    574 /* ARGSUSED */
    575 SIG_FUNC_RET
    576 sendwin(int sig)
    577 {
    578     if (connected) {
    579 	sendnaws();
    580     }
    581 }
    582 
    583 /* ARGSUSED */
    584 SIG_FUNC_RET
    585 ayt(int sig)
    586 {
    587     if (connected)
    588 	sendayt();
    589     else
    590 	ayt_status();
    591 }
    592 
    593 
    594 void
    596 sys_telnet_init(void)
    597 {
    598     (void) signal(SIGINT, intr);
    599     (void) signal(SIGQUIT, intr2);
    600     (void) signal(SIGPIPE, deadpeer);
    601     (void) signal(SIGWINCH, sendwin);
    602     (void) signal(SIGTSTP, susp);
    603     (void) signal(SIGINFO, ayt);
    604 
    605     setconnmode(0);
    606 
    607     NetNonblockingIO(net, 1);
    608 
    609 #ifdef TN3270
    610     if (noasynchnet == 0) {			/* DBX can't handle! */
    611 	NetSigIO(net, 1);
    612 	NetSetPgrp(net);
    613     }
    614 #endif	/* defined(TN3270) */
    615 
    616     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
    617 	perror("SetSockOpt");
    618     }
    619 }
    620 
    621 /*
    622  * Process rings -
    623  *
    624  *	This routine tries to fill up/empty our various rings.
    625  *
    626  *	The parameter specifies whether this is a poll operation,
    627  *	or a block-until-something-happens operation.
    628  *
    629  *	The return value is 1 if something happened, 0 if not, < 0 if an
    630  *	error occurred.
    631  */
    632 
    633 int
    634 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
    635     int dopoll)		/* If 0, then block until something to do */
    636 {
    637     struct pollfd set[3];
    638     int c;
    639 		/* One wants to be a bit careful about setting returnValue
    640 		 * to one, since a one implies we did some useful work,
    641 		 * and therefore probably won't be called to block next
    642 		 * time (TN3270 mode only).
    643 		 */
    644     int returnValue = 0;
    645 
    646     set[0].fd = net;
    647     set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
    648 	(netex ? POLLPRI : 0);
    649     set[1].fd = tout;
    650     set[1].events = ttyout ? POLLOUT : 0;
    651     set[2].fd = tin;
    652     set[2].events = ttyin ? POLLIN : 0;
    653 
    654     if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
    655 	if (c == -1) {
    656 		    /*
    657 		     * we can get EINTR if we are in line mode,
    658 		     * and the user does an escape (TSTP), or
    659 		     * some other signal generator.
    660 		     */
    661 	    if (errno == EINTR) {
    662 		return 0;
    663 	    }
    664 #ifdef TN3270
    665 		    /*
    666 		     * we can get EBADF if we were in transparent
    667 		     * mode, and the transcom process died.
    668 		    */
    669 	    if (errno == EBADF)
    670 		return 0;
    671 #endif /* defined(TN3270) */
    672 		    /* I don't like this, does it ever happen? */
    673 	    printf("sleep(5) from telnet, after poll\r\n");
    674 	    sleep(5);
    675 	}
    676 	return 0;
    677     }
    678 
    679     /*
    680      * Any urgent data?
    681      */
    682     if (set[0].revents & POLLPRI) {
    683 	SYNCHing = 1;
    684 	(void) ttyflush(1);	/* flush already enqueued data */
    685     }
    686 
    687     /*
    688      * Something to read from the network...
    689      */
    690     if (set[0].revents & POLLIN) {
    691 	int canread;
    692 
    693 	canread = ring_empty_consecutive(&netiring);
    694 	c = recv(net, (char *)netiring.supply, canread, 0);
    695 	if (c < 0 && errno == EWOULDBLOCK) {
    696 	    c = 0;
    697 	} else if (c <= 0) {
    698 	    return -1;
    699 	}
    700 	if (netdata) {
    701 	    Dump('<', netiring.supply, c);
    702 	}
    703 	if (c)
    704 	    ring_supplied(&netiring, c);
    705 	returnValue = 1;
    706     }
    707 
    708     /*
    709      * Something to read from the tty...
    710      */
    711     if (set[2].revents & POLLIN) {
    712 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
    713 	if (c < 0 && errno == EIO)
    714 	    c = 0;
    715 	if (c < 0 && errno == EWOULDBLOCK) {
    716 	    c = 0;
    717 	} else {
    718 	    if (c < 0) {
    719 		return -1;
    720 	    }
    721 	    if (c == 0) {
    722 		/* must be an EOF... */
    723 		if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
    724 		    *ttyiring.supply = termEofChar;
    725 		    c = 1;
    726 		} else {
    727 		    clienteof = 1;
    728 		    shutdown(net, 1);
    729 		    return 0;
    730 		}
    731 	    }
    732 	    if (termdata) {
    733 		Dump('<', ttyiring.supply, c);
    734 	    }
    735 	    ring_supplied(&ttyiring, c);
    736 	}
    737 	returnValue = 1;		/* did something useful */
    738     }
    739 
    740     if (set[0].revents & POLLOUT) {
    741 	returnValue |= netflush();
    742     }
    743     if (set[1].revents & POLLOUT) {
    744 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
    745     }
    746 
    747     return returnValue;
    748 }
    749