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