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