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