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