xref: /openbsd-src/usr.bin/telnet/sys_bsd.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: sys_bsd.c,v 1.16 2014/07/19 23:50:38 guenther Exp $	*/
2 /*	$NetBSD: sys_bsd.c,v 1.11 1996/02/28 21:04:10 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1988, 1990, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "telnet_locl.h"
34 #include <err.h>
35 
36 /*
37  * The following routines try to encapsulate what is system dependent
38  * (at least between 4.x and dos) which is used in telnet.c.
39  */
40 
41 int
42 	tout,			/* Output file descriptor */
43 	tin,			/* Input file descriptor */
44 	net;
45 
46 #define TELNET_FD_TOUT	0
47 #define TELNET_FD_TIN	1
48 #define TELNET_FD_NET	2
49 #define TELNET_FD_NUM	3
50 
51 struct	termios old_tc = { 0 };
52 extern struct termios new_tc;
53 
54     void
55 init_sys()
56 {
57     tout = fileno(stdout);
58     tin = fileno(stdin);
59 
60     errno = 0;
61 }
62 
63 
64     int
65 TerminalWrite(buf, n)
66     char *buf;
67     int  n;
68 {
69     return write(tout, buf, n);
70 }
71 
72     int
73 TerminalRead(buf, n)
74     unsigned char *buf;
75     int  n;
76 {
77     return read(tin, buf, n);
78 }
79 
80 /*
81  *
82  */
83 
84     int
85 TerminalAutoFlush()
86 {
87 #if	defined(LNOFLSH)
88     int flush;
89 
90     ioctl(0, TIOCLGET, (char *)&flush);
91     return !(flush&LNOFLSH);	/* if LNOFLSH, no autoflush */
92 #else	/* LNOFLSH */
93     return 1;
94 #endif	/* LNOFLSH */
95 }
96 
97 #ifdef	KLUDGELINEMODE
98 extern int kludgelinemode;
99 #endif
100 /*
101  * TerminalSpecialChars()
102  *
103  * Look at an input character to see if it is a special character
104  * and decide what to do.
105  *
106  * Output:
107  *
108  *	0	Don't add this character.
109  *	1	Do add this character
110  */
111 
112 extern void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
113 
114     int
115 TerminalSpecialChars(c)
116     int	c;
117 {
118     if (c == termIntChar) {
119 	intp();
120 	return 0;
121     } else if (c == termQuitChar) {
122 #ifdef	KLUDGELINEMODE
123 	if (kludgelinemode)
124 	    sendbrk();
125 	else
126 #endif
127 	    sendabort();
128 	return 0;
129     } else if (c == termEofChar) {
130 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
131 	    sendeof();
132 	    return 0;
133 	}
134 	return 1;
135     } else if (c == termSuspChar) {
136 	sendsusp();
137 	return(0);
138     } else if (c == termFlushChar) {
139 	xmitAO();		/* Transmit Abort Output */
140 	return 0;
141     } else if (!MODE_LOCAL_CHARS(globalmode)) {
142 	if (c == termKillChar) {
143 	    xmitEL();
144 	    return 0;
145 	} else if (c == termEraseChar) {
146 	    xmitEC();		/* Transmit Erase Character */
147 	    return 0;
148 	}
149     }
150     return 1;
151 }
152 
153 
154 /*
155  * Flush output to the terminal
156  */
157 
158     void
159 TerminalFlushOutput()
160 {
161 #ifdef	TIOCFLUSH
162     int com = FWRITE;
163     (void) ioctl(fileno(stdout), TIOCFLUSH, (int *) &com);
164 #else
165     (void) ioctl(fileno(stdout), TCFLSH, (int *) 0);
166 #endif
167 }
168 
169     void
170 TerminalSaveState()
171 {
172     tcgetattr(0, &old_tc);
173 
174     new_tc = old_tc;
175 
176 #ifndef	VDISCARD
177     termFlushChar = CONTROL('O');
178 #endif
179 #ifndef	VWERASE
180     termWerasChar = CONTROL('W');
181 #endif
182 #ifndef	VREPRINT
183     termRprntChar = CONTROL('R');
184 #endif
185 #ifndef	VLNEXT
186     termLiteralNextChar = CONTROL('V');
187 #endif
188 #ifndef	VSTART
189     termStartChar = CONTROL('Q');
190 #endif
191 #ifndef	VSTOP
192     termStopChar = CONTROL('S');
193 #endif
194 #ifndef	VSTATUS
195     termAytChar = CONTROL('T');
196 #endif
197 }
198 
199     cc_t *
200 tcval(func)
201     int func;
202 {
203     switch(func) {
204     case SLC_IP:	return(&termIntChar);
205     case SLC_ABORT:	return(&termQuitChar);
206     case SLC_EOF:	return(&termEofChar);
207     case SLC_EC:	return(&termEraseChar);
208     case SLC_EL:	return(&termKillChar);
209     case SLC_XON:	return(&termStartChar);
210     case SLC_XOFF:	return(&termStopChar);
211     case SLC_FORW1:	return(&termForw1Char);
212     case SLC_FORW2:	return(&termForw2Char);
213 # ifdef	VDISCARD
214     case SLC_AO:	return(&termFlushChar);
215 # endif
216 # ifdef	VSUSP
217     case SLC_SUSP:	return(&termSuspChar);
218 # endif
219 # ifdef	VWERASE
220     case SLC_EW:	return(&termWerasChar);
221 # endif
222 # ifdef	VREPRINT
223     case SLC_RP:	return(&termRprntChar);
224 # endif
225 # ifdef	VLNEXT
226     case SLC_LNEXT:	return(&termLiteralNextChar);
227 # endif
228 # ifdef	VSTATUS
229     case SLC_AYT:	return(&termAytChar);
230 # endif
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
241 TerminalDefaultChars()
242 {
243     memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
244 # ifndef	VDISCARD
245     termFlushChar = CONTROL('O');
246 # endif
247 # ifndef	VWERASE
248     termWerasChar = CONTROL('W');
249 # endif
250 # ifndef	VREPRINT
251     termRprntChar = CONTROL('R');
252 # endif
253 # ifndef	VLNEXT
254     termLiteralNextChar = CONTROL('V');
255 # endif
256 # ifndef	VSTART
257     termStartChar = CONTROL('Q');
258 # endif
259 # ifndef	VSTOP
260     termStopChar = CONTROL('S');
261 # endif
262 # ifndef	VSTATUS
263     termAytChar = CONTROL('T');
264 # endif
265 }
266 
267 /*
268  * TerminalNewMode - set up terminal to a specific mode.
269  *	MODE_ECHO: do local terminal echo
270  *	MODE_FLOW: do local flow control
271  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
272  *	MODE_EDIT: do local line editing
273  *
274  *	Command mode:
275  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
276  *		local echo
277  *		local editing
278  *		local xon/xoff
279  *		local signal mapping
280  *
281  *	Linemode:
282  *		local/no editing
283  *	Both Linemode and Single Character mode:
284  *		local/remote echo
285  *		local/no xon/xoff
286  *		local/no signal mapping
287  */
288 
289 #ifdef SIGTSTP
290 static void susp();
291 #endif /* SIGTSTP */
292 #ifdef SIGINFO
293 static void ayt();
294 #endif
295 
296     void
297 TerminalNewMode(f)
298     int f;
299 {
300     static int prevmode = 0;
301     struct termios tmp_tc;
302     int onoff;
303     int old;
304     cc_t esc;
305 
306     globalmode = f&~MODE_FORCE;
307     if (prevmode == f)
308 	return;
309 
310     /*
311      * Write any outstanding data before switching modes
312      * ttyflush() returns 0 only when there is no more data
313      * left to write out, it returns -1 if it couldn't do
314      * anything at all, otherwise it returns 1 + the number
315      * of characters left to write.
316      */
317     old = ttyflush(SYNCHing|flushout);
318     if (old < 0 || old > 1) {
319 	tcgetattr(tin, &tmp_tc);
320 	do {
321 	    /*
322 	     * Wait for data to drain, then flush again.
323 	     */
324 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
325 	    old = ttyflush(SYNCHing|flushout);
326 	} while (old < 0 || old > 1);
327     }
328 
329     old = prevmode;
330     prevmode = f&~MODE_FORCE;
331     tmp_tc = new_tc;
332 
333     if (f&MODE_ECHO) {
334 	tmp_tc.c_lflag |= ECHO;
335 	tmp_tc.c_oflag |= ONLCR;
336 	if (crlf)
337 		tmp_tc.c_iflag |= ICRNL;
338     } else {
339 	tmp_tc.c_lflag &= ~ECHO;
340 	tmp_tc.c_oflag &= ~ONLCR;
341     }
342 
343     if ((f&MODE_FLOW) == 0) {
344 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
345     } else {
346 	if (restartany < 0) {
347 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
348 	} else if (restartany > 0) {
349 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
350 	} else {
351 		tmp_tc.c_iflag |= IXOFF|IXON;
352 		tmp_tc.c_iflag &= ~IXANY;
353 	}
354     }
355 
356     if ((f&MODE_TRAPSIG) == 0) {
357 	tmp_tc.c_lflag &= ~ISIG;
358 	localchars = 0;
359     } else {
360 	tmp_tc.c_lflag |= ISIG;
361 	localchars = 1;
362     }
363 
364     if (f&MODE_EDIT) {
365 	tmp_tc.c_lflag |= ICANON;
366     } else {
367 	tmp_tc.c_lflag &= ~ICANON;
368 	tmp_tc.c_iflag &= ~ICRNL;
369 	tmp_tc.c_cc[VMIN] = 1;
370 	tmp_tc.c_cc[VTIME] = 0;
371     }
372 
373     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
374 	tmp_tc.c_lflag &= ~IEXTEN;
375     }
376 
377     if (f&MODE_SOFT_TAB) {
378 # ifdef	OXTABS
379 	tmp_tc.c_oflag |= OXTABS;
380 # endif
381 # ifdef	TABDLY
382 	tmp_tc.c_oflag &= ~TABDLY;
383 	tmp_tc.c_oflag |= TAB3;
384 # endif
385     } else {
386 # ifdef	OXTABS
387 	tmp_tc.c_oflag &= ~OXTABS;
388 # endif
389 # ifdef	TABDLY
390 	tmp_tc.c_oflag &= ~TABDLY;
391 # endif
392     }
393 
394     if (f&MODE_LIT_ECHO) {
395 # ifdef	ECHOCTL
396 	tmp_tc.c_lflag &= ~ECHOCTL;
397 # endif
398     } else {
399 # ifdef	ECHOCTL
400 	tmp_tc.c_lflag |= ECHOCTL;
401 # endif
402     }
403 
404     if (f == -1) {
405 	onoff = 0;
406     } else {
407 	if (f & MODE_INBIN)
408 		tmp_tc.c_iflag &= ~ISTRIP;
409 	else
410 		tmp_tc.c_iflag |= ISTRIP;
411 	if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
412 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
413 		tmp_tc.c_cflag |= CS8;
414 		if(f & MODE_OUTBIN)
415 			tmp_tc.c_oflag &= ~OPOST;
416 		else
417 			tmp_tc.c_oflag |= OPOST;
418 
419 	} else {
420 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
421 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
422 		tmp_tc.c_oflag |= OPOST;
423 	}
424 	onoff = 1;
425     }
426 
427     if (f != -1) {
428 #ifdef	SIGTSTP
429 	(void) signal(SIGTSTP, susp);
430 #endif	/* SIGTSTP */
431 #ifdef	SIGINFO
432 	(void) signal(SIGINFO, ayt);
433 #endif
434 #if	defined(NOKERNINFO)
435 	tmp_tc.c_lflag |= NOKERNINFO;
436 #endif
437 	/*
438 	 * We don't want to process ^Y here.  It's just another
439 	 * character that we'll pass on to the back end.  It has
440 	 * to process it because it will be processed when the
441 	 * user attempts to read it, not when we send it.
442 	 */
443 # ifdef	VDSUSP
444 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
445 # endif
446 	/*
447 	 * If the VEOL character is already set, then use VEOL2,
448 	 * otherwise use VEOL.
449 	 */
450 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
451 	if ((tmp_tc.c_cc[VEOL] != esc)
452 # ifdef	VEOL2
453 	    && (tmp_tc.c_cc[VEOL2] != esc)
454 # endif
455 	    ) {
456 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
457 		    tmp_tc.c_cc[VEOL] = esc;
458 # ifdef	VEOL2
459 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
460 		    tmp_tc.c_cc[VEOL2] = esc;
461 # endif
462 	}
463     } else {
464 #ifdef	SIGTSTP
465 	sigset_t mask;
466 #endif	/* SIGTSTP */
467 #ifdef	SIGINFO
468 	void ayt_status();
469 
470 	(void) signal(SIGINFO, (void (*)(int))ayt_status);
471 #endif
472 #ifdef	SIGTSTP
473 	(void) signal(SIGTSTP, SIG_DFL);
474 	sigemptyset(&mask);
475 	sigaddset(&mask, SIGTSTP);
476 	sigprocmask(SIG_UNBLOCK, &mask, NULL);
477 #endif	/* SIGTSTP */
478 	tmp_tc = old_tc;
479     }
480     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
481 	tcsetattr(tin, TCSANOW, &tmp_tc);
482 
483     ioctl(tin, FIONBIO, (char *)&onoff);
484     ioctl(tout, FIONBIO, (char *)&onoff);
485 }
486 
487 /*
488  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
489  */
490 #if B4800 != 4800
491 #define	DECODE_BAUD
492 #endif
493 
494 #ifdef	DECODE_BAUD
495 #ifndef	B7200
496 #define B7200   B4800
497 #endif
498 
499 #ifndef	B14400
500 #define B14400  B9600
501 #endif
502 
503 #ifndef	B19200
504 # define B19200 B14400
505 #endif
506 
507 #ifndef	B28800
508 #define B28800  B19200
509 #endif
510 
511 #ifndef	B38400
512 # define B38400 B28800
513 #endif
514 
515 #ifndef B57600
516 #define B57600  B38400
517 #endif
518 
519 #ifndef B76800
520 #define B76800  B57600
521 #endif
522 
523 #ifndef B115200
524 #define B115200 B76800
525 #endif
526 
527 #ifndef B230400
528 #define B230400 B115200
529 #endif
530 
531 
532 /*
533  * This code assumes that the values B0, B50, B75...
534  * are in ascending order.  They do not have to be
535  * contiguous.
536  */
537 struct termspeeds {
538 	long speed;
539 	long value;
540 } termspeeds[] = {
541 	{ 0,      B0 },      { 50,    B50 },    { 75,     B75 },
542 	{ 110,    B110 },    { 134,   B134 },   { 150,    B150 },
543 	{ 200,    B200 },    { 300,   B300 },   { 600,    B600 },
544 	{ 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
545 	{ 4800,   B4800 },   { 7200,  B7200 },  { 9600,   B9600 },
546 	{ 14400,  B14400 },  { 19200, B19200 }, { 28800,  B28800 },
547 	{ 38400,  B38400 },  { 57600, B57600 }, { 115200, B115200 },
548 	{ 230400, B230400 }, { -1,    B230400 }
549 };
550 #endif	/* DECODE_BAUD */
551 
552     void
553 TerminalSpeeds(ispeed, ospeed)
554     long *ispeed;
555     long *ospeed;
556 {
557 #ifdef	DECODE_BAUD
558     struct termspeeds *tp;
559 #endif	/* DECODE_BAUD */
560     long in, out;
561 
562     out = cfgetospeed(&old_tc);
563     in = cfgetispeed(&old_tc);
564     if (in == 0)
565 	in = out;
566 
567 #ifdef	DECODE_BAUD
568     tp = termspeeds;
569     while ((tp->speed != -1) && (tp->value < in))
570 	tp++;
571     *ispeed = tp->speed;
572 
573     tp = termspeeds;
574     while ((tp->speed != -1) && (tp->value < out))
575 	tp++;
576     *ospeed = tp->speed;
577 #else	/* DECODE_BAUD */
578 	*ispeed = in;
579 	*ospeed = out;
580 #endif	/* DECODE_BAUD */
581 }
582 
583     int
584 TerminalWindowSize(rows, cols)
585     long *rows, *cols;
586 {
587 #ifdef	TIOCGWINSZ
588     struct winsize ws;
589 
590     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
591 	*rows = ws.ws_row;
592 	*cols = ws.ws_col;
593 	return 1;
594     }
595 #endif	/* TIOCGWINSZ */
596     return 0;
597 }
598 
599     int
600 NetClose(fd)
601     int	fd;
602 {
603     return close(fd);
604 }
605 
606 
607     void
608 NetNonblockingIO(fd, onoff)
609     int fd;
610     int onoff;
611 {
612     ioctl(fd, FIONBIO, (char *)&onoff);
613 }
614 
615 
616 /*
617  * Various signal handling routines.
618  */
619 
620     /* ARGSUSED */
621     void
622 deadpeer(sig)
623     int sig;
624 {
625 	setcommandmode();
626 	longjmp(peerdied, -1);
627 }
628 
629 volatile sig_atomic_t intr_happened = 0;
630 volatile sig_atomic_t intr_waiting = 0;
631 
632     /* ARGSUSED */
633     void
634 intr(sig)
635     int sig;
636 {
637     if (intr_waiting) {
638 	intr_happened = 1;
639 	return;
640     }
641     if (localchars) {
642 	intp();
643 	return;
644     }
645     setcommandmode();
646     longjmp(toplevel, -1);
647 }
648 
649     /* ARGSUSED */
650     void
651 intr2(sig)
652     int sig;
653 {
654     if (localchars) {
655 #ifdef	KLUDGELINEMODE
656 	if (kludgelinemode)
657 	    sendbrk();
658 	else
659 #endif
660 	    sendabort();
661 	return;
662     }
663 }
664 
665 #ifdef	SIGTSTP
666     /* ARGSUSED */
667     void
668 susp(sig)
669     int sig;
670 {
671     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
672 	return;
673     if (localchars)
674 	sendsusp();
675 }
676 #endif
677 
678 #ifdef	SIGWINCH
679     /* ARGSUSED */
680     void
681 sendwin(sig)
682     int sig;
683 {
684     if (connected) {
685 	sendnaws();
686     }
687 }
688 #endif
689 
690 #ifdef	SIGINFO
691     /* ARGSUSED */
692     void
693 ayt(sig)
694     int sig;
695 {
696     if (connected)
697 	sendayt();
698     else
699 	ayt_status();
700 }
701 #endif
702 
703 
704     void
705 sys_telnet_init()
706 {
707     int one = 1;
708 
709     (void) signal(SIGINT, intr);
710     (void) signal(SIGQUIT, intr2);
711     (void) signal(SIGPIPE, deadpeer);
712 #ifdef	SIGWINCH
713     (void) signal(SIGWINCH, sendwin);
714 #endif
715 #ifdef	SIGTSTP
716     (void) signal(SIGTSTP, susp);
717 #endif
718 #ifdef	SIGINFO
719     (void) signal(SIGINFO, ayt);
720 #endif
721 
722     setconnmode(0);
723 
724     NetNonblockingIO(net, 1);
725 
726     if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) {
727 	perror("setsockopt");
728     }
729 }
730 
731 /*
732  * Process rings -
733  *
734  *	This routine tries to fill up/empty our various rings.
735  *
736  *	The parameter specifies whether this is a poll operation,
737  *	or a block-until-something-happens operation.
738  *
739  *	The return value is 1 if something happened, 0 if not.
740  */
741 
742     int
743 process_rings(netin, netout, netex, ttyin, ttyout, dopoll)
744     int dopoll;		/* If 0, then block until something to do */
745 {
746     int c;
747 		/* One wants to be a bit careful about setting returnValue
748 		 * to one, since a one implies we did some useful work,
749 		 * and therefore probably won't be called to block next
750 		 * time (TN3270 mode only).
751 		 */
752     int returnValue = 0;
753     struct pollfd pfd[TELNET_FD_NUM];
754 
755     if (ttyout) {
756 	pfd[TELNET_FD_TOUT].fd = tout;
757 	pfd[TELNET_FD_TOUT].events = POLLOUT;
758     } else {
759 	pfd[TELNET_FD_TOUT].fd = -1;
760     }
761     if (ttyin) {
762 	pfd[TELNET_FD_TIN].fd = tin;
763 	pfd[TELNET_FD_TIN].events = POLLIN;
764     } else {
765 	pfd[TELNET_FD_TIN].fd = -1;
766     }
767     if (netout || netin || netex) {
768 	pfd[TELNET_FD_NET].fd = net;
769 	pfd[TELNET_FD_NET].events = 0;
770 	if (netout)
771 	    pfd[TELNET_FD_NET].events |= POLLOUT;
772 	if (netin)
773 	    pfd[TELNET_FD_NET].events |= POLLIN;
774 	if (netex)
775 	    pfd[TELNET_FD_NET].events |= POLLRDBAND;
776     } else {
777 	pfd[TELNET_FD_NET].fd = -1;
778     }
779 
780     if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : -1)) < 0) {
781 	if (c == -1) {
782 		    /*
783 		     * we can get EINTR if we are in line mode,
784 		     * and the user does an escape (TSTP), or
785 		     * some other signal generator.
786 		     */
787 	    if (errno == EINTR) {
788 		return 0;
789 	    }
790 		    /* I don't like this, does it ever happen? */
791 	    printf("sleep(5) from telnet, after poll\r\n");
792 	    sleep(5);
793 	}
794 	return 0;
795     }
796 
797     /*
798      * Any urgent data?
799      */
800     if (pfd[TELNET_FD_NET].revents & POLLRDBAND) {
801 	SYNCHing = 1;
802 	(void) ttyflush(1);	/* flush already enqueued data */
803     }
804 
805     /*
806      * Something to read from the network...
807      */
808     if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) {
809 	int canread;
810 
811 	canread = ring_empty_consecutive(&netiring);
812 	c = recv(net, (char *)netiring.supply, canread, 0);
813 	if (c < 0 && errno == EWOULDBLOCK) {
814 	    c = 0;
815 	} else if (c <= 0) {
816 	    return -1;
817 	}
818 	if (netdata) {
819 	    Dump('<', netiring.supply, c);
820 	}
821 	if (c)
822 	    ring_supplied(&netiring, c);
823 	returnValue = 1;
824     }
825 
826     /*
827      * Something to read from the tty...
828      */
829     if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) {
830 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
831 	if (c < 0 && errno == EIO)
832 	    c = 0;
833 	if (c < 0 && errno == EWOULDBLOCK) {
834 	    c = 0;
835 	} else {
836 	    /* EOF detection for line mode!!!! */
837 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
838 		/* must be an EOF... */
839 		*ttyiring.supply = termEofChar;
840 		c = 1;
841 	    }
842 	    if (c <= 0) {
843 		return -1;
844 	    }
845 	    if (termdata) {
846 		Dump('<', ttyiring.supply, c);
847 	    }
848 	    ring_supplied(&ttyiring, c);
849 	}
850 	returnValue = 1;		/* did something useful */
851     }
852 
853     if (pfd[TELNET_FD_NET].revents & POLLOUT) {
854 	returnValue |= netflush();
855     }
856     if (pfd[TELNET_FD_TOUT].revents & POLLOUT) {
857 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
858     }
859 
860     return returnValue;
861 }
862