xref: /openbsd/usr.bin/telnet/sys_bsd.c (revision a6445c1d)
1 /*	$OpenBSD: sys_bsd.c,v 1.28 2014/09/09 03:41:08 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 
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <arpa/telnet.h>
38 #include <errno.h>
39 #include <poll.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 /*
44  * The following routines try to encapsulate what is system dependent
45  * (at least between 4.x and dos) which is used in telnet.c.
46  */
47 
48 int
49 	tout,			/* Output file descriptor */
50 	tin,			/* Input file descriptor */
51 	net;
52 
53 #define TELNET_FD_TOUT	0
54 #define TELNET_FD_TIN	1
55 #define TELNET_FD_NET	2
56 #define TELNET_FD_NUM	3
57 
58 struct	termios old_tc = { 0 };
59 
60 void
61 init_sys(void)
62 {
63     tout = fileno(stdout);
64     tin = fileno(stdin);
65 
66     errno = 0;
67 }
68 
69 
70 /*
71  * TerminalSpecialChars()
72  *
73  * Look at an input character to see if it is a special character
74  * and decide what to do.
75  *
76  * Output:
77  *
78  *	0	Don't add this character.
79  *	1	Do add this character
80  */
81 
82 int
83 TerminalSpecialChars(int c)
84 {
85     if (c == termIntChar) {
86 	intp();
87 	return 0;
88     } else if (c == termQuitChar) {
89 #ifdef	KLUDGELINEMODE
90 	if (kludgelinemode)
91 	    sendbrk();
92 	else
93 #endif
94 	    sendabort();
95 	return 0;
96     } else if (c == termEofChar) {
97 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
98 	    sendeof();
99 	    return 0;
100 	}
101 	return 1;
102     } else if (c == termSuspChar) {
103 	sendsusp();
104 	return(0);
105     } else if (c == termFlushChar) {
106 	xmitAO();		/* Transmit Abort Output */
107 	return 0;
108     } else if (!MODE_LOCAL_CHARS(globalmode)) {
109 	if (c == termKillChar) {
110 	    xmitEL();
111 	    return 0;
112 	} else if (c == termEraseChar) {
113 	    xmitEC();		/* Transmit Erase Character */
114 	    return 0;
115 	}
116     }
117     return 1;
118 }
119 
120 void
121 TerminalSaveState(void)
122 {
123     tcgetattr(0, &old_tc);
124 
125     new_tc = old_tc;
126 
127 #ifndef	VDISCARD
128     termFlushChar = CONTROL('O');
129 #endif
130 #ifndef	VWERASE
131     termWerasChar = CONTROL('W');
132 #endif
133 #ifndef	VREPRINT
134     termRprntChar = CONTROL('R');
135 #endif
136 #ifndef	VLNEXT
137     termLiteralNextChar = CONTROL('V');
138 #endif
139 #ifndef	VSTART
140     termStartChar = CONTROL('Q');
141 #endif
142 #ifndef	VSTOP
143     termStopChar = CONTROL('S');
144 #endif
145 #ifndef	VSTATUS
146     termAytChar = CONTROL('T');
147 #endif
148 }
149 
150 cc_t *
151 tcval(int func)
152 {
153     switch(func) {
154     case SLC_IP:	return(&termIntChar);
155     case SLC_ABORT:	return(&termQuitChar);
156     case SLC_EOF:	return(&termEofChar);
157     case SLC_EC:	return(&termEraseChar);
158     case SLC_EL:	return(&termKillChar);
159     case SLC_XON:	return(&termStartChar);
160     case SLC_XOFF:	return(&termStopChar);
161     case SLC_FORW1:	return(&termForw1Char);
162     case SLC_FORW2:	return(&termForw2Char);
163     case SLC_SUSP:	return(&termSuspChar);
164 # ifdef	VDISCARD
165     case SLC_AO:	return(&termFlushChar);
166 # endif
167 # ifdef	VWERASE
168     case SLC_EW:	return(&termWerasChar);
169 # endif
170 # ifdef	VREPRINT
171     case SLC_RP:	return(&termRprntChar);
172 # endif
173 # ifdef	VLNEXT
174     case SLC_LNEXT:	return(&termLiteralNextChar);
175 # endif
176 # ifdef	VSTATUS
177     case SLC_AYT:	return(&termAytChar);
178 # endif
179 
180     case SLC_SYNCH:
181     case SLC_BRK:
182     case SLC_EOR:
183     default:
184 	return((cc_t *)0);
185     }
186 }
187 
188 void
189 TerminalDefaultChars(void)
190 {
191     memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
192 # ifndef	VDISCARD
193     termFlushChar = CONTROL('O');
194 # endif
195 # ifndef	VWERASE
196     termWerasChar = CONTROL('W');
197 # endif
198 # ifndef	VREPRINT
199     termRprntChar = CONTROL('R');
200 # endif
201 # ifndef	VLNEXT
202     termLiteralNextChar = CONTROL('V');
203 # endif
204 # ifndef	VSTART
205     termStartChar = CONTROL('Q');
206 # endif
207 # ifndef	VSTOP
208     termStopChar = CONTROL('S');
209 # endif
210 # ifndef	VSTATUS
211     termAytChar = CONTROL('T');
212 # endif
213 }
214 
215 /*
216  * TerminalNewMode - set up terminal to a specific mode.
217  *	MODE_ECHO: do local terminal echo
218  *	MODE_FLOW: do local flow control
219  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
220  *	MODE_EDIT: do local line editing
221  *
222  *	Command mode:
223  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
224  *		local echo
225  *		local editing
226  *		local xon/xoff
227  *		local signal mapping
228  *
229  *	Linemode:
230  *		local/no editing
231  *	Both Linemode and Single Character mode:
232  *		local/remote echo
233  *		local/no xon/xoff
234  *		local/no signal mapping
235  */
236 
237 static void susp();
238 #ifdef SIGINFO
239 static void ayt();
240 #endif
241 
242 void
243 TerminalNewMode(int f)
244 {
245     static int prevmode = 0;
246     struct termios tmp_tc;
247     int onoff;
248     int old;
249     cc_t esc;
250 
251     globalmode = f&~MODE_FORCE;
252     if (prevmode == f)
253 	return;
254 
255     /*
256      * Write any outstanding data before switching modes
257      * ttyflush() returns 0 only when there is no more data
258      * left to write out, it returns -1 if it couldn't do
259      * anything at all, otherwise it returns 1 + the number
260      * of characters left to write.
261      */
262     old = ttyflush(SYNCHing|flushout);
263     if (old < 0 || old > 1) {
264 	tcgetattr(tin, &tmp_tc);
265 	do {
266 	    /*
267 	     * Wait for data to drain, then flush again.
268 	     */
269 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
270 	    old = ttyflush(SYNCHing|flushout);
271 	} while (old < 0 || old > 1);
272     }
273 
274     old = prevmode;
275     prevmode = f&~MODE_FORCE;
276     tmp_tc = new_tc;
277 
278     if (f&MODE_ECHO) {
279 	tmp_tc.c_lflag |= ECHO;
280 	tmp_tc.c_oflag |= ONLCR;
281 	if (crlf)
282 		tmp_tc.c_iflag |= ICRNL;
283     } else {
284 	tmp_tc.c_lflag &= ~ECHO;
285 	tmp_tc.c_oflag &= ~ONLCR;
286     }
287 
288     if ((f&MODE_FLOW) == 0) {
289 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
290     } else {
291 	if (restartany < 0) {
292 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
293 	} else if (restartany > 0) {
294 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
295 	} else {
296 		tmp_tc.c_iflag |= IXOFF|IXON;
297 		tmp_tc.c_iflag &= ~IXANY;
298 	}
299     }
300 
301     if ((f&MODE_TRAPSIG) == 0) {
302 	tmp_tc.c_lflag &= ~ISIG;
303 	localchars = 0;
304     } else {
305 	tmp_tc.c_lflag |= ISIG;
306 	localchars = 1;
307     }
308 
309     if (f&MODE_EDIT) {
310 	tmp_tc.c_lflag |= ICANON;
311     } else {
312 	tmp_tc.c_lflag &= ~ICANON;
313 	tmp_tc.c_iflag &= ~ICRNL;
314 	tmp_tc.c_cc[VMIN] = 1;
315 	tmp_tc.c_cc[VTIME] = 0;
316     }
317 
318     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
319 	tmp_tc.c_lflag &= ~IEXTEN;
320     }
321 
322     if (f&MODE_SOFT_TAB) {
323 # ifdef	OXTABS
324 	tmp_tc.c_oflag |= OXTABS;
325 # endif
326 # ifdef	TABDLY
327 	tmp_tc.c_oflag &= ~TABDLY;
328 	tmp_tc.c_oflag |= TAB3;
329 # endif
330     } else {
331 # ifdef	OXTABS
332 	tmp_tc.c_oflag &= ~OXTABS;
333 # endif
334 # ifdef	TABDLY
335 	tmp_tc.c_oflag &= ~TABDLY;
336 # endif
337     }
338 
339     if (f&MODE_LIT_ECHO) {
340 # ifdef	ECHOCTL
341 	tmp_tc.c_lflag &= ~ECHOCTL;
342 # endif
343     } else {
344 # ifdef	ECHOCTL
345 	tmp_tc.c_lflag |= ECHOCTL;
346 # endif
347     }
348 
349     if (f == -1) {
350 	onoff = 0;
351     } else {
352 	if (f & MODE_INBIN)
353 		tmp_tc.c_iflag &= ~ISTRIP;
354 	else
355 		tmp_tc.c_iflag |= ISTRIP;
356 	if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
357 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
358 		tmp_tc.c_cflag |= CS8;
359 		if(f & MODE_OUTBIN)
360 			tmp_tc.c_oflag &= ~OPOST;
361 		else
362 			tmp_tc.c_oflag |= OPOST;
363 
364 	} else {
365 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
366 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
367 		tmp_tc.c_oflag |= OPOST;
368 	}
369 	onoff = 1;
370     }
371 
372     if (f != -1) {
373 	(void) signal(SIGTSTP, susp);
374 #ifdef	SIGINFO
375 	(void) signal(SIGINFO, ayt);
376 #endif
377 #if	defined(NOKERNINFO)
378 	tmp_tc.c_lflag |= NOKERNINFO;
379 #endif
380 	/*
381 	 * We don't want to process ^Y here.  It's just another
382 	 * character that we'll pass on to the back end.  It has
383 	 * to process it because it will be processed when the
384 	 * user attempts to read it, not when we send it.
385 	 */
386 # ifdef	VDSUSP
387 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
388 # endif
389 	/*
390 	 * If the VEOL character is already set, then use VEOL2,
391 	 * otherwise use VEOL.
392 	 */
393 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
394 	if ((tmp_tc.c_cc[VEOL] != esc)
395 # ifdef	VEOL2
396 	    && (tmp_tc.c_cc[VEOL2] != esc)
397 # endif
398 	    ) {
399 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
400 		    tmp_tc.c_cc[VEOL] = esc;
401 # ifdef	VEOL2
402 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
403 		    tmp_tc.c_cc[VEOL2] = esc;
404 # endif
405 	}
406     } else {
407 	sigset_t mask;
408 #ifdef	SIGINFO
409 	(void) signal(SIGINFO, ayt_status);
410 #endif
411 	(void) signal(SIGTSTP, SIG_DFL);
412 	sigemptyset(&mask);
413 	sigaddset(&mask, SIGTSTP);
414 	sigprocmask(SIG_UNBLOCK, &mask, NULL);
415 	tmp_tc = old_tc;
416     }
417     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
418 	tcsetattr(tin, TCSANOW, &tmp_tc);
419 
420     ioctl(tin, FIONBIO, &onoff);
421     ioctl(tout, FIONBIO, &onoff);
422 }
423 
424 /*
425  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
426  */
427 #if B4800 != 4800
428 #define	DECODE_BAUD
429 #endif
430 
431 #ifdef	DECODE_BAUD
432 #ifndef	B7200
433 #define B7200   B4800
434 #endif
435 
436 #ifndef	B14400
437 #define B14400  B9600
438 #endif
439 
440 #ifndef	B19200
441 # define B19200 B14400
442 #endif
443 
444 #ifndef	B28800
445 #define B28800  B19200
446 #endif
447 
448 #ifndef	B38400
449 # define B38400 B28800
450 #endif
451 
452 #ifndef B57600
453 #define B57600  B38400
454 #endif
455 
456 #ifndef B76800
457 #define B76800  B57600
458 #endif
459 
460 #ifndef B115200
461 #define B115200 B76800
462 #endif
463 
464 #ifndef B230400
465 #define B230400 B115200
466 #endif
467 
468 
469 /*
470  * This code assumes that the values B0, B50, B75...
471  * are in ascending order.  They do not have to be
472  * contiguous.
473  */
474 struct termspeeds {
475 	long speed;
476 	long value;
477 } termspeeds[] = {
478 	{ 0,      B0 },      { 50,    B50 },    { 75,     B75 },
479 	{ 110,    B110 },    { 134,   B134 },   { 150,    B150 },
480 	{ 200,    B200 },    { 300,   B300 },   { 600,    B600 },
481 	{ 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
482 	{ 4800,   B4800 },   { 7200,  B7200 },  { 9600,   B9600 },
483 	{ 14400,  B14400 },  { 19200, B19200 }, { 28800,  B28800 },
484 	{ 38400,  B38400 },  { 57600, B57600 }, { 115200, B115200 },
485 	{ 230400, B230400 }, { -1,    B230400 }
486 };
487 #endif	/* DECODE_BAUD */
488 
489 void
490 TerminalSpeeds(long *ispeed, long *ospeed)
491 {
492 #ifdef	DECODE_BAUD
493     struct termspeeds *tp;
494 #endif	/* DECODE_BAUD */
495     long in, out;
496 
497     out = cfgetospeed(&old_tc);
498     in = cfgetispeed(&old_tc);
499     if (in == 0)
500 	in = out;
501 
502 #ifdef	DECODE_BAUD
503     tp = termspeeds;
504     while ((tp->speed != -1) && (tp->value < in))
505 	tp++;
506     *ispeed = tp->speed;
507 
508     tp = termspeeds;
509     while ((tp->speed != -1) && (tp->value < out))
510 	tp++;
511     *ospeed = tp->speed;
512 #else	/* DECODE_BAUD */
513 	*ispeed = in;
514 	*ospeed = out;
515 #endif	/* DECODE_BAUD */
516 }
517 
518 int
519 TerminalWindowSize(long *rows, long *cols)
520 {
521 #ifdef	TIOCGWINSZ
522     struct winsize ws;
523 
524     if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
525 	*rows = ws.ws_row;
526 	*cols = ws.ws_col;
527 	return 1;
528     }
529 #endif	/* TIOCGWINSZ */
530     return 0;
531 }
532 
533 /*
534  * Various signal handling routines.
535  */
536 
537 void
538 deadpeer(int sig)
539 {
540 	setcommandmode();
541 	longjmp(peerdied, -1);
542 }
543 
544 void
545 intr(int sig)
546 {
547     if (localchars) {
548 	intp();
549 	return;
550     }
551     setcommandmode();
552     longjmp(toplevel, -1);
553 }
554 
555 void
556 intr2(int sig)
557 {
558     if (localchars) {
559 #ifdef	KLUDGELINEMODE
560 	if (kludgelinemode)
561 	    sendbrk();
562 	else
563 #endif
564 	    sendabort();
565 	return;
566     }
567 }
568 
569 void
570 susp(int sig)
571 {
572     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
573 	return;
574     if (localchars)
575 	sendsusp();
576 }
577 
578 #ifdef	SIGWINCH
579 void
580 sendwin(int sig)
581 {
582     if (connected) {
583 	sendnaws();
584     }
585 }
586 #endif
587 
588 #ifdef	SIGINFO
589 void
590 ayt(int sig)
591 {
592     if (connected)
593 	sendayt();
594     else
595 	ayt_status(sig);
596 }
597 #endif
598 
599 
600 void
601 sys_telnet_init(void)
602 {
603     int one = 1;
604 
605     (void) signal(SIGINT, intr);
606     (void) signal(SIGQUIT, intr2);
607     (void) signal(SIGPIPE, deadpeer);
608 #ifdef	SIGWINCH
609     (void) signal(SIGWINCH, sendwin);
610 #endif
611     (void) signal(SIGTSTP, susp);
612 #ifdef	SIGINFO
613     (void) signal(SIGINFO, ayt);
614 #endif
615 
616     setconnmode(0);
617 
618     /*
619      * Mark the socket as non-blocking and receive urgent data inline.
620      * (The latter is required for correct telnet operation when a
621      * second urgent is sent before telnet can process the first.)
622      */
623     ioctl(net, FIONBIO, &one);
624     if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) {
625 	perror("setsockopt");
626     }
627 }
628 
629 /*
630  * Process rings -
631  *
632  *	This routine tries to fill up/empty our various rings.
633  *
634  *	The parameter specifies whether this is a poll operation,
635  *	or a block-until-something-happens operation.
636  *
637  *	The return value is 1 if something happened, 0 if not.
638  */
639 
640 int
641 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
642     int dopoll)		/* If 0, then block until something to do */
643 {
644     int c;
645 		/* One wants to be a bit careful about setting returnValue
646 		 * to one, since a one implies we did some useful work,
647 		 * and therefore probably won't be called to block next
648 		 * time (TN3270 mode only).
649 		 */
650     int returnValue = 0;
651     struct pollfd pfd[TELNET_FD_NUM];
652 
653     if (ttyout) {
654 	pfd[TELNET_FD_TOUT].fd = tout;
655 	pfd[TELNET_FD_TOUT].events = POLLOUT;
656     } else {
657 	pfd[TELNET_FD_TOUT].fd = -1;
658     }
659     if (ttyin) {
660 	pfd[TELNET_FD_TIN].fd = tin;
661 	pfd[TELNET_FD_TIN].events = POLLIN;
662     } else {
663 	pfd[TELNET_FD_TIN].fd = -1;
664     }
665     if (netout || netin || netex) {
666 	pfd[TELNET_FD_NET].fd = net;
667 	pfd[TELNET_FD_NET].events = 0;
668 	if (netout)
669 	    pfd[TELNET_FD_NET].events |= POLLOUT;
670 	if (netin)
671 	    pfd[TELNET_FD_NET].events |= POLLIN;
672 	if (netex)
673 	    pfd[TELNET_FD_NET].events |= POLLRDBAND;
674     } else {
675 	pfd[TELNET_FD_NET].fd = -1;
676     }
677 
678     if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) < 0) {
679 	return 0;
680     }
681 
682     /*
683      * Any urgent data?
684      */
685     if (pfd[TELNET_FD_NET].revents & POLLRDBAND) {
686 	SYNCHing = 1;
687 	(void) ttyflush(1);	/* flush already enqueued data */
688     }
689 
690     /*
691      * Something to read from the network...
692      */
693     if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) {
694 	int canread;
695 
696 	canread = ring_empty_consecutive(&netiring);
697 	c = recv(net, netiring.supply, canread, 0);
698 	if (c < 0 && errno == EWOULDBLOCK) {
699 	    c = 0;
700 	} else if (c <= 0) {
701 	    return -1;
702 	}
703 	if (netdata) {
704 	    Dump('<', netiring.supply, c);
705 	}
706 	if (c)
707 	    ring_supplied(&netiring, c);
708 	returnValue = 1;
709     }
710 
711     /*
712      * Something to read from the tty...
713      */
714     if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) {
715 	c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring));
716 	if (c < 0 && errno == EIO)
717 	    c = 0;
718 	if (c < 0 && errno == EWOULDBLOCK) {
719 	    c = 0;
720 	} else {
721 	    /* EOF detection for line mode!!!! */
722 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
723 		/* must be an EOF... */
724 		*ttyiring.supply = termEofChar;
725 		c = 1;
726 	    }
727 	    if (c <= 0) {
728 		return -1;
729 	    }
730 	    if (termdata) {
731 		Dump('<', ttyiring.supply, c);
732 	    }
733 	    ring_supplied(&ttyiring, c);
734 	}
735 	returnValue = 1;		/* did something useful */
736     }
737 
738     if (pfd[TELNET_FD_NET].revents & POLLOUT) {
739 	returnValue |= netflush();
740     }
741     if (pfd[TELNET_FD_TOUT].revents & POLLOUT) {
742 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
743     }
744 
745     return returnValue;
746 }
747