xref: /original-bsd/usr.bin/telnet/sys_bsd.c (revision 6ab384a1)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)sys_bsd.c	1.28 (Berkeley) 07/28/90";
10 #endif /* not lint */
11 
12 /*
13  * The following routines try to encapsulate what is system dependent
14  * (at least between 4.x and dos) which is used in telnet.c.
15  */
16 
17 
18 #include <fcntl.h>
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/socket.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <arpa/telnet.h>
25 
26 #include "ring.h"
27 
28 #include "fdset.h"
29 
30 #include "defines.h"
31 #include "externs.h"
32 #include "types.h"
33 
34 #if	defined(CRAY)
35 #define	SIG_FUNC_RET	void
36 #else
37 #define	SIG_FUNC_RET	int
38 #endif
39 
40 int
41 	tout,			/* Output file descriptor */
42 	tin,			/* Input file descriptor */
43 	net;
44 
45 #ifndef	USE_TERMIO
46 struct	tchars otc = { 0 }, ntc = { 0 };
47 struct	ltchars oltc = { 0 }, nltc = { 0 };
48 struct	sgttyb ottyb = { 0 }, nttyb = { 0 };
49 int	olmode = 0;
50 
51 #else	/* USE_TERMIO */
52 struct	termio old_tc = { 0 };
53 extern struct termio new_tc;
54 
55 #ifndef	TCGETA
56 # ifdef TCGETS
57 #  define	TCGETA	TCGETS
58 #  define	TCSETA	TCSETS
59 #  define	TCSETAW	TCSETSW
60 # else
61 #  define	TCGETA	TIOCGETA
62 #  define	TCSETA	TIOCSETA
63 #  define	TCSETAW	TIOCSETAW
64 # endif
65 #endif	/* TCGETA */
66 #endif	/* USE_TERMIO */
67 
68 static fd_set ibits, obits, xbits;
69 
70 
71 init_sys()
72 {
73     tout = fileno(stdout);
74     tin = fileno(stdin);
75     FD_ZERO(&ibits);
76     FD_ZERO(&obits);
77     FD_ZERO(&xbits);
78 
79     errno = 0;
80 }
81 
82 
83 TerminalWrite(buf, n)
84 char	*buf;
85 int	n;
86 {
87     return write(tout, buf, n);
88 }
89 
90 TerminalRead(buf, n)
91 char	*buf;
92 int	n;
93 {
94     return read(tin, buf, n);
95 }
96 
97 /*
98  *
99  */
100 
101 int
102 TerminalAutoFlush()
103 {
104 #if	defined(LNOFLSH)
105     int flush;
106 
107     ioctl(0, TIOCLGET, (char *)&flush);
108     return !(flush&LNOFLSH);	/* if LNOFLSH, no autoflush */
109 #else	/* LNOFLSH */
110     return 1;
111 #endif	/* LNOFLSH */
112 }
113 
114 #ifdef	KLUDGELINEMODE
115 extern int kludgelinemode;
116 #endif
117 /*
118  * TerminalSpecialChars()
119  *
120  * Look at an input character to see if it is a special character
121  * and decide what to do.
122  *
123  * Output:
124  *
125  *	0	Don't add this character.
126  *	1	Do add this character
127  */
128 
129 int
130 TerminalSpecialChars(c)
131 int	c;
132 {
133     void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
134 
135     if (c == termIntChar) {
136 	intp();
137 	return 0;
138     } else if (c == termQuitChar) {
139 #ifdef	KLUDGELINEMODE
140 	if (kludgelinemode)
141 	    sendbrk();
142 	else
143 #endif
144 	    sendabort();
145 	return 0;
146     } else if (c == termEofChar) {
147 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
148 	    sendeof();
149 	    return 0;
150 	}
151 	return 1;
152     } else if (c == termSuspChar) {
153 	sendsusp();
154 	return(0);
155     } else if (c == termFlushChar) {
156 	xmitAO();		/* Transmit Abort Output */
157 	return 0;
158     } else if (!MODE_LOCAL_CHARS(globalmode)) {
159 	if (c == termKillChar) {
160 	    xmitEL();
161 	    return 0;
162 	} else if (c == termEraseChar) {
163 	    xmitEC();		/* Transmit Erase Character */
164 	    return 0;
165 	}
166     }
167     return 1;
168 }
169 
170 
171 /*
172  * Flush output to the terminal
173  */
174 
175 void
176 TerminalFlushOutput()
177 {
178 #ifdef	TIOCFLUSH
179     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
180 #else
181     (void) ioctl(fileno(stdout), TCFLSH, (char *) 0);
182 #endif
183 }
184 
185 void
186 TerminalSaveState()
187 {
188 #ifndef	USE_TERMIO
189     ioctl(0, TIOCGETP, (char *)&ottyb);
190     ioctl(0, TIOCGETC, (char *)&otc);
191     ioctl(0, TIOCGLTC, (char *)&oltc);
192     ioctl(0, TIOCLGET, (char *)&olmode);
193 
194     ntc = otc;
195     nltc = oltc;
196     nttyb = ottyb;
197 
198 #else	/* USE_TERMIO */
199     ioctl(0, TCGETA, &old_tc);
200 
201     new_tc = old_tc;
202 
203     termFlushChar = CONTROL('O');
204     termWerasChar = CONTROL('W');
205     termRprntChar = CONTROL('R');
206     termLiteralNextChar = CONTROL('V');
207     termStartChar = CONTROL('Q');
208     termStopChar = CONTROL('S');
209 #endif	/* USE_TERMIO */
210 }
211 
212 cc_t *
213 tcval(func)
214 register int func;
215 {
216     switch(func) {
217     case SLC_IP:	return(&termIntChar);
218     case SLC_ABORT:	return(&termQuitChar);
219     case SLC_EOF:	return(&termEofChar);
220     case SLC_EC:	return(&termEraseChar);
221     case SLC_EL:	return(&termKillChar);
222     case SLC_XON:	return(&termStartChar);
223     case SLC_XOFF:	return(&termStopChar);
224     case SLC_FORW1:	return(&termForw1Char);
225 #ifndef	SYSV_TERMIO
226     case SLC_AO:	return(&termFlushChar);
227     case SLC_SUSP:	return(&termSuspChar);
228     case SLC_EW:	return(&termWerasChar);
229     case SLC_RP:	return(&termRprntChar);
230     case SLC_LNEXT:	return(&termLiteralNextChar);
231 #endif	/* SYSV_TERMIO */
232 #ifdef	USE_TERMIO
233     case SLC_FORW2:	return(&termForw2Char);
234 #endif
235 
236     case SLC_SYNCH:
237     case SLC_BRK:
238     case SLC_AYT:
239     case SLC_EOR:
240     default:
241 	return((cc_t *)0);
242     }
243 }
244 
245 void
246 TerminalDefaultChars()
247 {
248 #ifndef	USE_TERMIO
249     ntc = otc;
250     nltc = oltc;
251     nttyb.sg_kill = ottyb.sg_kill;
252     nttyb.sg_erase = ottyb.sg_erase;
253 #else	/* USE_TERMIO */
254     memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
255 # ifndef	VFLUSHO
256     termFlushChar = CONTROL('O');
257 # endif
258 # ifndef	VWERASE
259     termWerasChar = CONTROL('W');
260 # endif
261 # ifndef	VREPRINT
262     termRprntChar = CONTROL('R');
263 # endif
264 # ifndef	VLNEXT
265     termLiteralNextChar = CONTROL('V');
266 # endif
267 # ifndef	VSTART
268     termStartChar = CONTROL('Q');
269 # endif
270 # ifndef	VSTOP
271     termStopChar = CONTROL('S');
272 # endif
273 #endif	/* USE_TERMIO */
274 }
275 
276 #ifdef notdef
277 void
278 TerminalRestoreState()
279 {
280 }
281 #endif
282 
283 /*
284  * TerminalNewMode - set up terminal to a specific mode.
285  *	MODE_ECHO: do local terminal echo
286  *	MODE_FLOW: do local flow control
287  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
288  *	MODE_EDIT: do local line editing
289  *
290  *	Command mode:
291  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
292  *		local echo
293  *		local editing
294  *		local xon/xoff
295  *		local signal mapping
296  *
297  *	Linemode:
298  *		local/no editing
299  *	Both Linemode and Single Character mode:
300  *		local/remote echo
301  *		local/no xon/xoff
302  *		local/no signal mapping
303  */
304 
305 
306 void
307 TerminalNewMode(f)
308 register int f;
309 {
310     static int prevmode = 0;
311 #ifndef	USE_TERMIO
312     struct tchars tc;
313     struct ltchars ltc;
314     struct sgttyb sb;
315     int lmode;
316 #else	/* USE_TERMIO */
317     struct termio tmp_tc;
318 #endif	/* USE_TERMIO */
319     int onoff;
320     int old;
321 
322     globalmode = f&~MODE_FORCE;
323     if (prevmode == f)
324 	return;
325 
326     /*
327      * Write any outstanding data before switching modes
328      * ttyflush() returns 0 only when there is no more data
329      * left to write out, it returns -1 if it couldn't do
330      * anything at all, otherwise it returns 1 + the number
331      * of characters left to write.
332      */
333     old = ttyflush(SYNCHing|flushout);
334     if (old < 0 || old > 1) {
335 #ifndef	USE_TERMIO
336 	ioctl(tin, TIOCGETP, (char *)&sb);
337 #else	/* USE_TERMIO */
338 	ioctl(tin, TCGETA, (char *)&tmp_tc);
339 #endif	/* USE_TERMIO */
340 	do {
341 	    /*
342 	     * Wait for data to drain, then flush again.
343 	     */
344 #ifndef	USE_TERMIO
345 	    ioctl(tin, TIOCSETP, (char *)&sb);
346 #else	/* USE_TERMIO */
347 	    ioctl(tin, TCSETAW, (char *)&tmp_tc);
348 #endif	/* USE_TERMIO */
349 	    old = ttyflush(SYNCHing|flushout);
350 	} while (old < 0 || old > 1);
351     }
352 
353     old = prevmode;
354     prevmode = f&~MODE_FORCE;
355 #ifndef	USE_TERMIO
356     sb = nttyb;
357     tc = ntc;
358     ltc = nltc;
359     lmode = olmode;
360 #else
361     tmp_tc = new_tc;
362 #endif
363 
364     if (f&MODE_ECHO) {
365 #ifndef	USE_TERMIO
366 	sb.sg_flags |= ECHO;
367 #else
368 	tmp_tc.c_lflag |= ECHO;
369 	tmp_tc.c_oflag |= ONLCR;
370 # ifdef notdef
371 	tmp_tc.c_iflag |= ICRNL;
372 # endif
373 #endif
374     } else {
375 #ifndef	USE_TERMIO
376 	sb.sg_flags &= ~ECHO;
377 #else
378 	tmp_tc.c_lflag &= ~ECHO;
379 	tmp_tc.c_oflag &= ~ONLCR;
380 # ifdef notdef
381 	tmp_tc.c_iflag &= ~ICRNL;
382 # endif
383 #endif
384     }
385 
386     if ((f&MODE_FLOW) == 0) {
387 #ifndef	USE_TERMIO
388 	tc.t_startc = -1;
389 	tc.t_stopc = -1;
390 #else
391 	tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON);
392     } else {
393 	tmp_tc.c_iflag |= IXANY|IXOFF|IXON;
394 #endif
395     }
396 
397     if ((f&MODE_TRAPSIG) == 0) {
398 #ifndef	USE_TERMIO
399 	tc.t_intrc = -1;
400 	tc.t_quitc = -1;
401 	tc.t_eofc = -1;
402 	ltc.t_suspc = -1;
403 	ltc.t_dsuspc = -1;
404 #else
405 	tmp_tc.c_lflag &= ~ISIG;
406 #endif
407 	localchars = 0;
408     } else {
409 #ifdef	USE_TERMIO
410 	tmp_tc.c_lflag |= ISIG;
411 #endif
412 	localchars = 1;
413     }
414 
415     if (f&MODE_EDIT) {
416 #ifndef	USE_TERMIO
417 	sb.sg_flags &= ~CBREAK;
418 	sb.sg_flags |= CRMOD;
419 #else
420 	tmp_tc.c_lflag |= ICANON;
421 #endif
422     } else {
423 #ifndef	USE_TERMIO
424 	sb.sg_flags |= CBREAK;
425 	if (f&MODE_ECHO)
426 	    sb.sg_flags |= CRMOD;
427 	else
428 	    sb.sg_flags &= ~CRMOD;
429 #else
430 	tmp_tc.c_lflag &= ~ICANON;
431 	tmp_tc.c_iflag &= ~ICRNL;
432 	tmp_tc.c_cc[VMIN] = 1;
433 	tmp_tc.c_cc[VTIME] = 0;
434 #endif
435     }
436 
437     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
438 #ifndef	USE_TERMIO
439 	ltc.t_lnextc = -1;
440 #else
441 # ifdef VLNEXT
442 	tmp_tc.c_cc[VLNEXT] = (cc_t)(-1);
443 # endif
444 #endif
445     }
446 
447     if (f&MODE_SOFT_TAB) {
448 #ifndef USE_TERMIO
449 	sb.sg_flags |= XTABS;
450 #else
451 # ifdef	OXTABS
452 	tmp_tc.c_oflag |= OXTABS;
453 # endif
454 # ifdef	TABDLY
455 	tmp_tc.c_oflag &= ~TABDLY;
456 	tmp_tc.c_oflag |= TAB3;
457 # endif
458 #endif
459     } else {
460 #ifndef USE_TERMIO
461 	sb.sg_flags &= ~XTABS;
462 #else
463 # ifdef	OXTABS
464 	tmp_tc.c_oflag &= ~OXTABS;
465 # endif
466 # ifdef	TABDLY
467 	tmp_tc.c_oflag &= ~TABDLY;
468 # endif
469 #endif
470     }
471 
472     if (f&MODE_LIT_ECHO) {
473 #ifndef USE_TERMIO
474 	sb.sg_flags &= ~CTLECH;
475 #else
476 # ifdef	ECHOCTL
477 	tmp_tc.c_lflag &= ~ECHOCTL;
478 # endif
479 #endif
480     } else {
481 #ifndef USE_TERMIO
482 	sb.sg_flags |= CTLECH;
483 #else
484 # ifdef	ECHOCTL
485 	tmp_tc.c_lflag |= ECHOCTL;
486 # endif
487 #endif
488     }
489 
490     if (f == -1) {
491 	onoff = 0;
492     } else {
493 #ifndef	USE_TERMIO
494 	if (f & MODE_OUTBIN)
495 		lmode |= LLITOUT;
496 	else
497 		lmode &= ~LLITOUT;
498 
499 	if (f & MODE_INBIN)
500 		lmode |= LPASS8;
501 	else
502 		lmode &= ~LPASS8;
503 #else
504 	if (f & MODE_OUTBIN)
505 		tmp_tc.c_lflag &= ~ISTRIP;
506 	else
507 		tmp_tc.c_lflag |= ISTRIP;
508 	if (f & MODE_INBIN) {
509 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
510 		tmp_tc.c_cflag |= CS8;
511 		tmp_tc.c_cflag &= ~OPOST;
512 	} else {
513 		tmp_tc.c_cflag &= ~CSIZE;
514 		tmp_tc.c_cflag |= CS7|PARENB;
515 		tmp_tc.c_cflag |= OPOST;
516 	}
517 #endif
518 	onoff = 1;
519     }
520 
521     if (f != -1) {
522 #ifdef	SIGTSTP
523 	static void susp();
524 
525 	(void) signal(SIGTSTP, (SIG_FUNC_RET (*)())susp);
526 #endif	/* SIGTSTP */
527 	/*
528 	 * We don't want to process ^Y here.  It's just another
529 	 * character that we'll pass on to the back end.  It has
530 	 * to process it because it will be processed when the
531 	 * user attempts to read it, not when we send it.
532 	 */
533 #ifndef	USE_TERMIO
534 	ltc.t_dsuspc = -1;
535 #else
536 # ifdef	VDSUSP
537 	tmp_tc.c_cc[VDSUSP] = (cc_t)(-1);
538 # endif
539 #endif
540 #ifdef USE_TERMIO
541 /*
542  * Disable VSTATUS (^T)
543  */
544 # ifdef VSTATUS
545 	tmp_tc.c_cc[VSTATUS] = (cc_t)(-1);
546 # endif
547 #endif
548 #ifdef	USE_TERMIO
549 	/*
550 	 * If the VEOL character is already set, then use VEOL2,
551 	 * otherwise use VEOL.
552 	 */
553 	if (tmp_tc.c_cc[VEOL] == (cc_t)(-1))
554 		tmp_tc.c_cc[VEOL] = escape;
555 # ifdef	VEOL2
556 	else if (tmp_tc.c_cc[VEOL2] == (cc_t)(-1))
557 		tmp_tc.c_cc[VEOL2] = escape;
558 # endif
559 #else
560 	if (tc.t_brkc == (cc_t)(-1))
561 		tc.t_brkc = escape;
562 #endif
563     } else {
564 #ifdef	SIGTSTP
565 	(void) signal(SIGTSTP, SIG_DFL);
566 	(void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
567 #endif	/* SIGTSTP */
568 #ifndef USE_TERMIO
569 	ltc = oltc;
570 	tc = otc;
571 	sb = ottyb;
572 	lmode = olmode;
573 #else
574 	tmp_tc = old_tc;
575 #endif
576     }
577 #ifndef USE_TERMIO
578     ioctl(tin, TIOCLSET, (char *)&lmode);
579     ioctl(tin, TIOCSLTC, (char *)&ltc);
580     ioctl(tin, TIOCSETC, (char *)&tc);
581     ioctl(tin, TIOCSETP, (char *)&sb);
582 #else
583     if (ioctl(tin, TCSETAW, &tmp_tc) < 0)
584 	ioctl(tin, TCSETA, &tmp_tc);
585 #endif
586 
587 #if	(!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
588     ioctl(tin, FIONBIO, (char *)&onoff);
589     ioctl(tout, FIONBIO, (char *)&onoff);
590 #endif	/* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
591 #if	defined(TN3270)
592     if (noasynchtty == 0) {
593 	ioctl(tin, FIOASYNC, (char *)&onoff);
594     }
595 #endif	/* defined(TN3270) */
596 }
597 
598 #ifndef	B19200
599 # define B19200 B9600
600 #endif
601 
602 #ifndef	B38400
603 # define B38400 B19200
604 #endif
605 
606 /*
607  * This code assumes that the values B0, B50, B75...
608  * are in ascending order.  They do not have to be
609  * contiguous.
610  */
611 struct termspeeds {
612 	long speed;
613 	long value;
614 } termspeeds[] = {
615 	{ 0,     B0 },     { 50,    B50 },   { 75,    B75 },
616 	{ 110,   B110 },   { 134,   B134 },  { 150,   B150 },
617 	{ 200,   B200 },   { 300,   B300 },  { 600,   B600 },
618 	{ 1200,  B1200 },  { 1800,  B1800 }, { 2400,  B2400 },
619 	{ 4800,  B4800 },  { 9600,  B9600 }, { 19200, B19200 },
620 	{ 38400, B38400 }, { -1,    B38400 }
621 };
622 
623 #ifndef	USE_TERMIO
624 # define	ISPEED	ottyb.sg_ispeed
625 # define	OSPEED	ottyb.sg_ospeed
626 #else
627 # ifdef	CBAUD
628 #  define	ISPEED	(old_tc.c_cflag&CBAUD)
629 #  define	OSPEED	ISPEED
630 # else
631 #  define	ISPEED	old_tc.c_ispeed
632 #  define OSPEED	old_tc.c_ospeed
633 # endif
634 #endif
635 
636 void
637 TerminalSpeeds(ispeed, ospeed)
638 long *ispeed;
639 long *ospeed;
640 {
641     register struct termspeeds *tp;
642 
643     tp = termspeeds;
644     while ((tp->speed != -1) && (tp->value < ISPEED))
645 	tp++;
646     *ispeed = tp->speed;
647 
648     tp = termspeeds;
649     while ((tp->speed != -1) && (tp->value < OSPEED))
650 	tp++;
651     *ospeed = tp->speed;
652 }
653 
654 int
655 TerminalWindowSize(rows, cols)
656 long *rows, *cols;
657 {
658 #ifdef	TIOCGWINSZ
659     struct winsize ws;
660 
661     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
662 	*rows = ws.ws_row;
663 	*cols = ws.ws_col;
664 	return 1;
665     }
666 #endif	/* TIOCGWINSZ */
667     return 0;
668 }
669 
670 int
671 NetClose(fd)
672 int	fd;
673 {
674     return close(fd);
675 }
676 
677 
678 void
679 NetNonblockingIO(fd, onoff)
680 int
681 	fd,
682 	onoff;
683 {
684     ioctl(fd, FIONBIO, (char *)&onoff);
685 }
686 
687 #if	defined(TN3270)
688 void
689 NetSigIO(fd, onoff)
690 int
691 	fd,
692 	onoff;
693 {
694     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
695 }
696 
697 void
698 NetSetPgrp(fd)
699 int fd;
700 {
701     int myPid;
702 
703     myPid = getpid();
704     fcntl(fd, F_SETOWN, myPid);
705 }
706 #endif	/*defined(TN3270)*/
707 
708 /*
709  * Various signal handling routines.
710  */
711 
712 static void
713 deadpeer()
714 {
715 	setcommandmode();
716 	longjmp(peerdied, -1);
717 }
718 
719 static void
720 intr()
721 {
722     if (localchars) {
723 	intp();
724 	return;
725     }
726     setcommandmode();
727     longjmp(toplevel, -1);
728 }
729 
730 static void
731 intr2()
732 {
733     if (localchars) {
734 #ifdef	KLUDGELINEMODE
735 	if (kludgelinemode)
736 	    sendbrk();
737 	else
738 #endif
739 	    sendabort();
740 	return;
741     }
742 }
743 
744 static void
745 susp()
746 {
747     if (localchars)
748 	sendsusp();
749 }
750 
751 static void
752 sendwin()
753 {
754     if (connected) {
755 	sendnaws();
756     }
757 }
758 
759 
760 void
761 sys_telnet_init()
762 {
763     (void) signal(SIGINT, (SIG_FUNC_RET (*)())intr);
764     (void) signal(SIGQUIT, (SIG_FUNC_RET (*)())intr2);
765     (void) signal(SIGPIPE, (SIG_FUNC_RET (*)())deadpeer);
766 #ifdef	SIGWINCH
767     (void) signal(SIGWINCH, (SIG_FUNC_RET (*)())sendwin);
768 #endif
769 #ifdef	SIGTSTP
770     (void) signal(SIGTSTP, (SIG_FUNC_RET (*)())susp);
771 #endif
772 
773     setconnmode(0);
774 
775     NetNonblockingIO(net, 1);
776 
777 #if	defined(TN3270)
778     if (noasynchnet == 0) {			/* DBX can't handle! */
779 	NetSigIO(net, 1);
780 	NetSetPgrp(net);
781     }
782 #endif	/* defined(TN3270) */
783 
784 #if	defined(SO_OOBINLINE)
785     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
786 	perror("SetSockOpt");
787     }
788 #endif	/* defined(SO_OOBINLINE) */
789 }
790 
791 /*
792  * Process rings -
793  *
794  *	This routine tries to fill up/empty our various rings.
795  *
796  *	The parameter specifies whether this is a poll operation,
797  *	or a block-until-something-happens operation.
798  *
799  *	The return value is 1 if something happened, 0 if not.
800  */
801 
802 int
803 process_rings(netin, netout, netex, ttyin, ttyout, poll)
804 int poll;		/* If 0, then block until something to do */
805 {
806     register int c;
807 		/* One wants to be a bit careful about setting returnValue
808 		 * to one, since a one implies we did some useful work,
809 		 * and therefore probably won't be called to block next
810 		 * time (TN3270 mode only).
811 		 */
812     int returnValue = 0;
813     static struct timeval TimeValue = { 0 };
814 
815     if (netout) {
816 	FD_SET(net, &obits);
817     }
818     if (ttyout) {
819 	FD_SET(tout, &obits);
820     }
821 #if	defined(TN3270)
822     if (ttyin) {
823 	FD_SET(tin, &ibits);
824     }
825 #else	/* defined(TN3270) */
826     if (ttyin) {
827 	FD_SET(tin, &ibits);
828     }
829 #endif	/* defined(TN3270) */
830 #if	defined(TN3270)
831     if (netin) {
832 	FD_SET(net, &ibits);
833     }
834 #   else /* !defined(TN3270) */
835     if (netin) {
836 	FD_SET(net, &ibits);
837     }
838 #   endif /* !defined(TN3270) */
839     if (netex) {
840 	FD_SET(net, &xbits);
841     }
842     if ((c = select(16, &ibits, &obits, &xbits,
843 			(poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
844 	if (c == -1) {
845 		    /*
846 		     * we can get EINTR if we are in line mode,
847 		     * and the user does an escape (TSTP), or
848 		     * some other signal generator.
849 		     */
850 	    if (errno == EINTR) {
851 		return 0;
852 	    }
853 #	    if defined(TN3270)
854 		    /*
855 		     * we can get EBADF if we were in transparent
856 		     * mode, and the transcom process died.
857 		    */
858 	    if (errno == EBADF) {
859 			/*
860 			 * zero the bits (even though kernel does it)
861 			 * to make sure we are selecting on the right
862 			 * ones.
863 			*/
864 		FD_ZERO(&ibits);
865 		FD_ZERO(&obits);
866 		FD_ZERO(&xbits);
867 		return 0;
868 	    }
869 #	    endif /* defined(TN3270) */
870 		    /* I don't like this, does it ever happen? */
871 	    printf("sleep(5) from telnet, after select\r\n");
872 	    sleep(5);
873 	}
874 	return 0;
875     }
876 
877     /*
878      * Any urgent data?
879      */
880     if (FD_ISSET(net, &xbits)) {
881 	FD_CLR(net, &xbits);
882 	SYNCHing = 1;
883 	(void) ttyflush(1);	/* flush already enqueued data */
884     }
885 
886     /*
887      * Something to read from the network...
888      */
889     if (FD_ISSET(net, &ibits)) {
890 	int canread;
891 
892 	FD_CLR(net, &ibits);
893 	canread = ring_empty_consecutive(&netiring);
894 #if	!defined(SO_OOBINLINE)
895 	    /*
896 	     * In 4.2 (and some early 4.3) systems, the
897 	     * OOB indication and data handling in the kernel
898 	     * is such that if two separate TCP Urgent requests
899 	     * come in, one byte of TCP data will be overlaid.
900 	     * This is fatal for Telnet, but we try to live
901 	     * with it.
902 	     *
903 	     * In addition, in 4.2 (and...), a special protocol
904 	     * is needed to pick up the TCP Urgent data in
905 	     * the correct sequence.
906 	     *
907 	     * What we do is:  if we think we are in urgent
908 	     * mode, we look to see if we are "at the mark".
909 	     * If we are, we do an OOB receive.  If we run
910 	     * this twice, we will do the OOB receive twice,
911 	     * but the second will fail, since the second
912 	     * time we were "at the mark", but there wasn't
913 	     * any data there (the kernel doesn't reset
914 	     * "at the mark" until we do a normal read).
915 	     * Once we've read the OOB data, we go ahead
916 	     * and do normal reads.
917 	     *
918 	     * There is also another problem, which is that
919 	     * since the OOB byte we read doesn't put us
920 	     * out of OOB state, and since that byte is most
921 	     * likely the TELNET DM (data mark), we would
922 	     * stay in the TELNET SYNCH (SYNCHing) state.
923 	     * So, clocks to the rescue.  If we've "just"
924 	     * received a DM, then we test for the
925 	     * presence of OOB data when the receive OOB
926 	     * fails (and AFTER we did the normal mode read
927 	     * to clear "at the mark").
928 	     */
929 	if (SYNCHing) {
930 	    int atmark;
931 	    static int bogus_oob = 0, first = 1;
932 
933 	    ioctl(net, SIOCATMARK, (char *)&atmark);
934 	    if (atmark) {
935 		c = recv(net, netiring.supply, canread, MSG_OOB);
936 		if ((c == -1) && (errno == EINVAL)) {
937 		    c = recv(net, netiring.supply, canread, 0);
938 		    if (clocks.didnetreceive < clocks.gotDM) {
939 			SYNCHing = stilloob(net);
940 		    }
941 		} else if (first && c > 0) {
942 		    /*
943 		     * Bogosity check.  Systems based on 4.2BSD
944 		     * do not return an error if you do a second
945 		     * recv(MSG_OOB).  So, we do one.  If it
946 		     * succeeds and returns exactly the same
947 		     * data, then assume that we are running
948 		     * on a broken system and set the bogus_oob
949 		     * flag.  (If the data was different, then
950 		     * we probably got some valid new data, so
951 		     * increment the count...)
952 		     */
953 		    int i;
954 		    i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
955 		    if (i == c &&
956 			  bcmp(netiring.supply, netiring.supply + c, i) == 0) {
957 			bogus_oob = 1;
958 			first = 0;
959 		    } else if (i < 0) {
960 			bogus_oob = 0;
961 			first = 0;
962 		    } else
963 			c += i;
964 		}
965 		if (bogus_oob && c > 0) {
966 		    int i;
967 		    /*
968 		     * Bogosity.  We have to do the read
969 		     * to clear the atmark to get out of
970 		     * an infinate loop.
971 		     */
972 		    i = read(net, netiring.supply + c, canread - c);
973 		    if (i > 0)
974 			c += i;
975 		}
976 	    } else {
977 		c = recv(net, netiring.supply, canread, 0);
978 	    }
979 	} else {
980 	    c = recv(net, netiring.supply, canread, 0);
981 	}
982 	settimer(didnetreceive);
983 #else	/* !defined(SO_OOBINLINE) */
984 	c = recv(net, netiring.supply, canread, 0);
985 #endif	/* !defined(SO_OOBINLINE) */
986 	if (c < 0 && errno == EWOULDBLOCK) {
987 	    c = 0;
988 	} else if (c <= 0) {
989 	    return -1;
990 	}
991 	if (netdata) {
992 	    Dump('<', netiring.supply, c);
993 	}
994 	if (c)
995 	    ring_supplied(&netiring, c);
996 	returnValue = 1;
997     }
998 
999     /*
1000      * Something to read from the tty...
1001      */
1002     if (FD_ISSET(tin, &ibits)) {
1003 	FD_CLR(tin, &ibits);
1004 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
1005 	if (c < 0 && errno == EWOULDBLOCK) {
1006 	    c = 0;
1007 	} else {
1008 	    /* EOF detection for line mode!!!! */
1009 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
1010 			/* must be an EOF... */
1011 		*ttyiring.supply = termEofChar;
1012 		c = 1;
1013 	    }
1014 	    if (c <= 0) {
1015 		return -1;
1016 	    }
1017 	    if (termdata) {
1018 		Dump('<', ttyiring.supply, c);
1019 	    }
1020 	    ring_supplied(&ttyiring, c);
1021 	}
1022 	returnValue = 1;		/* did something useful */
1023     }
1024 
1025     if (FD_ISSET(net, &obits)) {
1026 	FD_CLR(net, &obits);
1027 	returnValue |= netflush();
1028     }
1029     if (FD_ISSET(tout, &obits)) {
1030 	FD_CLR(tout, &obits);
1031 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
1032     }
1033 
1034     return returnValue;
1035 }
1036