xref: /netbsd-src/usr.bin/telnet/sys_bsd.c (revision da5f4674a3fc214be3572d358b66af40ab9401e7)
1 /*	$NetBSD: sys_bsd.c,v 1.29 2003/08/07 11:16:10 agc Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 from: static char sccsid[] = "@(#)sys_bsd.c	8.4 (Berkeley) 5/30/95";
36 #else
37 __RCSID("$NetBSD: sys_bsd.c,v 1.29 2003/08/07 11:16:10 agc Exp $");
38 #endif
39 #endif /* not lint */
40 
41 /*
42  * The following routines try to encapsulate what is system dependent
43  * (at least between 4.x and dos) which is used in telnet.c.
44  */
45 
46 
47 #include <fcntl.h>
48 #include <sys/types.h>
49 #include <sys/time.h>
50 #include <sys/socket.h>
51 #include <signal.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <poll.h>
56 #include <arpa/telnet.h>
57 
58 #include "ring.h"
59 #include "defines.h"
60 #include "externs.h"
61 #include "types.h"
62 
63 #define	SIG_FUNC_RET	void
64 
65 SIG_FUNC_RET susp(int);
66 SIG_FUNC_RET ayt(int);
67 
68 SIG_FUNC_RET intr(int);
69 SIG_FUNC_RET intr2(int);
70 SIG_FUNC_RET sendwin(int);
71 SIG_FUNC_RET deadpeer(int);
72 
73 
74 int
75 	tout,			/* Output file descriptor */
76 	tin,			/* Input file descriptor */
77 	net;
78 
79 struct	termios old_tc = { 0 };
80 extern struct termios new_tc;
81 
82 # ifndef	TCSANOW
83 #  ifdef TCSETS
84 #   define	TCSANOW		TCSETS
85 #   define	TCSADRAIN	TCSETSW
86 #   define	tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
87 #  else
88 #   ifdef TCSETA
89 #    define	TCSANOW		TCSETA
90 #    define	TCSADRAIN	TCSETAW
91 #    define	tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
92 #   else
93 #    define	TCSANOW		TIOCSETA
94 #    define	TCSADRAIN	TIOCSETAW
95 #    define	tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
96 #   endif
97 #  endif
98 #  define	tcsetattr(f, a, t) ioctl(f, a, (char *)t)
99 #  define	cfgetospeed(ptr)	((ptr)->c_cflag&CBAUD)
100 #  ifdef CIBAUD
101 #   define	cfgetispeed(ptr)	(((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
102 #  else
103 #   define	cfgetispeed(ptr)	cfgetospeed(ptr)
104 #  endif
105 # endif /* TCSANOW */
106 
107 
108 void
109 init_sys(void)
110 {
111     tout = fileno(stdout);
112     tin = fileno(stdin);
113 
114     errno = 0;
115 }
116 
117 
118 int
119 TerminalWrite(char *buf, int  n)
120 {
121     return write(tout, buf, n);
122 }
123 
124 int
125 TerminalRead(unsigned char *buf, int  n)
126 {
127     return read(tin, buf, n);
128 }
129 
130 /*
131  *
132  */
133 
134 int
135 TerminalAutoFlush(void)
136 {
137     return 1;
138 }
139 
140 #ifdef	KLUDGELINEMODE
141 extern int kludgelinemode;
142 #endif
143 /*
144  * TerminalSpecialChars()
145  *
146  * Look at an input character to see if it is a special character
147  * and decide what to do.
148  *
149  * Output:
150  *
151  *	0	Don't add this character.
152  *	1	Do add this character
153  */
154 
155 int
156 TerminalSpecialChars(int c)
157 {
158     if (c == termIntChar) {
159 	intp();
160 	return 0;
161     } else if (c == termQuitChar) {
162 #ifdef	KLUDGELINEMODE
163 	if (kludgelinemode)
164 	    sendbrk();
165 	else
166 #endif
167 	    sendabort();
168 	return 0;
169     } else if (c == termEofChar) {
170 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
171 	    sendeof();
172 	    return 0;
173 	}
174 	return 1;
175     } else if (c == termSuspChar) {
176 	sendsusp();
177 	return(0);
178     } else if (c == termFlushChar) {
179 	xmitAO();		/* Transmit Abort Output */
180 	return 0;
181     } else if (!MODE_LOCAL_CHARS(globalmode)) {
182 	if (c == termKillChar) {
183 	    xmitEL();
184 	    return 0;
185 	} else if (c == termEraseChar) {
186 	    xmitEC();		/* Transmit Erase Character */
187 	    return 0;
188 	}
189     }
190     return 1;
191 }
192 
193 
194 /*
195  * Flush output to the terminal
196  */
197 
198 void
199 TerminalFlushOutput(void)
200 {
201     int com = 0;
202     (void) ioctl(fileno(stdout), TIOCFLUSH, &com);
203 }
204 
205 void
206 TerminalSaveState(void)
207 {
208     tcgetattr(0, &old_tc);
209 
210     new_tc = old_tc;
211 }
212 
213 cc_t *
214 tcval(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     case SLC_FORW2:	return(&termForw2Char);
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     case SLC_AYT:	return(&termAytChar);
232 
233     case SLC_SYNCH:
234     case SLC_BRK:
235     case SLC_EOR:
236     default:
237 	return((cc_t *)0);
238     }
239 }
240 
241 void
242 TerminalDefaultChars(void)
243 {
244     memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
245 }
246 
247 #ifdef notdef
248 void
249 TerminalRestoreState(void)
250 {
251 }
252 #endif
253 
254 /*
255  * TerminalNewMode - set up terminal to a specific mode.
256  *	MODE_ECHO: do local terminal echo
257  *	MODE_FLOW: do local flow control
258  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
259  *	MODE_EDIT: do local line editing
260  *
261  *	Command mode:
262  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
263  *		local echo
264  *		local editing
265  *		local xon/xoff
266  *		local signal mapping
267  *
268  *	Linemode:
269  *		local/no editing
270  *	Both Linemode and Single Character mode:
271  *		local/remote echo
272  *		local/no xon/xoff
273  *		local/no signal mapping
274  */
275 
276 
277 void
278 TerminalNewMode(int f)
279 {
280     static int prevmode = 0;
281     struct termios tmp_tc;
282     int onoff;
283     int old;
284     cc_t esc;
285 
286     globalmode = f&~MODE_FORCE;
287     if (prevmode == f)
288 	return;
289 
290     /*
291      * Write any outstanding data before switching modes
292      * ttyflush() returns 0 only when there is no more data
293      * left to write out, it returns -1 if it couldn't do
294      * anything at all, otherwise it returns 1 + the number
295      * of characters left to write.
296 #ifndef	USE_TERMIO
297      * We would really like to ask the kernel to wait for the output
298      * to drain, like we can do with the TCSADRAIN, but we don't have
299      * that option.  The only ioctl that waits for the output to
300      * drain, TIOCSETP, also flushes the input queue, which is NOT
301      * what we want (TIOCSETP is like TCSADFLUSH).
302 #endif
303      */
304     old = ttyflush(SYNCHing|flushout);
305     if (old < 0 || old > 1) {
306 	tcgetattr(tin, &tmp_tc);
307 	do {
308 	    /*
309 	     * Wait for data to drain, then flush again.
310 	     */
311 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
312 	    old = ttyflush(SYNCHing|flushout);
313 	    if (old == -2)
314 		return;
315 	} while (old < 0 || old > 1);
316     }
317 
318     old = prevmode;
319     prevmode = f&~MODE_FORCE;
320     tmp_tc = new_tc;
321 
322     if (f&MODE_ECHO) {
323 	tmp_tc.c_lflag |= ECHO;
324 	tmp_tc.c_oflag |= ONLCR;
325 	if (crlf)
326 		tmp_tc.c_iflag |= ICRNL;
327     } else {
328 	tmp_tc.c_lflag &= ~ECHO;
329 	tmp_tc.c_oflag &= ~ONLCR;
330 # ifdef notdef
331 	if (crlf)
332 		tmp_tc.c_iflag &= ~ICRNL;
333 # endif
334     }
335 
336     if ((f&MODE_FLOW) == 0) {
337 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
338     } else {
339 	if (restartany < 0) {
340 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
341 	} else if (restartany > 0) {
342 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
343 	} else {
344 		tmp_tc.c_iflag |= IXOFF|IXON;
345 		tmp_tc.c_iflag &= ~IXANY;
346 	}
347     }
348 
349     if ((f&MODE_TRAPSIG) == 0) {
350 	tmp_tc.c_lflag &= ~ISIG;
351 	localchars = 0;
352     } else {
353 	tmp_tc.c_lflag |= ISIG;
354 	localchars = 1;
355     }
356 
357     if (f&MODE_EDIT) {
358 	tmp_tc.c_lflag |= ICANON;
359     } else {
360 	tmp_tc.c_lflag &= ~ICANON;
361 	tmp_tc.c_iflag &= ~ICRNL;
362 	tmp_tc.c_cc[VMIN] = 1;
363 	tmp_tc.c_cc[VTIME] = 0;
364     }
365 
366     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
367 	tmp_tc.c_lflag &= ~IEXTEN;
368     }
369 
370     if (f&MODE_SOFT_TAB) {
371 # ifdef	OXTABS
372 	tmp_tc.c_oflag |= OXTABS;
373 # endif
374 # ifdef	TABDLY
375 	tmp_tc.c_oflag &= ~TABDLY;
376 	tmp_tc.c_oflag |= TAB3;
377 # endif
378     } else {
379 # ifdef	OXTABS
380 	tmp_tc.c_oflag &= ~OXTABS;
381 # endif
382 # ifdef	TABDLY
383 	tmp_tc.c_oflag &= ~TABDLY;
384 # endif
385     }
386 
387     if (f&MODE_LIT_ECHO) {
388 # ifdef	ECHOCTL
389 	tmp_tc.c_lflag &= ~ECHOCTL;
390 # endif
391     } else {
392 # ifdef	ECHOCTL
393 	tmp_tc.c_lflag |= ECHOCTL;
394 # endif
395     }
396 
397     if (f == -1) {
398 	onoff = 0;
399     } else {
400 	if (f & MODE_INBIN)
401 		tmp_tc.c_iflag &= ~ISTRIP;
402 	else
403 		tmp_tc.c_iflag |= ISTRIP;
404 	if (f & MODE_OUTBIN) {
405 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
406 		tmp_tc.c_cflag |= CS8;
407 		tmp_tc.c_oflag &= ~OPOST;
408 	} else {
409 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
410 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
411 		tmp_tc.c_oflag |= OPOST;
412 	}
413 	onoff = 1;
414     }
415 
416     if (f != -1) {
417 	(void) signal(SIGTSTP, susp);
418 	(void) signal(SIGINFO, ayt);
419 #if	defined(USE_TERMIO) && defined(NOKERNINFO)
420 	tmp_tc.c_lflag |= NOKERNINFO;
421 #endif
422 	/*
423 	 * We don't want to process ^Y here.  It's just another
424 	 * character that we'll pass on to the back end.  It has
425 	 * to process it because it will be processed when the
426 	 * user attempts to read it, not when we send it.
427 	 */
428 # ifdef	VDSUSP
429 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
430 # endif
431 	/*
432 	 * If the VEOL character is already set, then use VEOL2,
433 	 * otherwise use VEOL.
434 	 */
435 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
436 	if ((tmp_tc.c_cc[VEOL] != esc)
437 # ifdef	VEOL2
438 	    && (tmp_tc.c_cc[VEOL2] != esc)
439 # endif
440 	    ) {
441 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
442 		    tmp_tc.c_cc[VEOL] = esc;
443 # ifdef	VEOL2
444 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
445 		    tmp_tc.c_cc[VEOL2] = esc;
446 # endif
447 	}
448     } else {
449 	(void) signal(SIGINFO, (void (*)(int)) ayt_status);
450 	(void) signal(SIGTSTP, SIG_DFL);
451 	(void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
452 	tmp_tc = old_tc;
453     }
454     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
455 	tcsetattr(tin, TCSANOW, &tmp_tc);
456 
457     ioctl(tin, FIONBIO, (char *)&onoff);
458     ioctl(tout, FIONBIO, (char *)&onoff);
459 #if	defined(TN3270)
460     if (noasynchtty == 0) {
461 	ioctl(tin, FIOASYNC, (char *)&onoff);
462     }
463 #endif	/* defined(TN3270) */
464 
465 }
466 
467 void
468 TerminalSpeeds(long *ispeed, long *ospeed)
469 {
470     long in, out;
471 
472     out = cfgetospeed(&old_tc);
473     in = cfgetispeed(&old_tc);
474     if (in == 0)
475 	in = out;
476 
477 	*ispeed = in;
478 	*ospeed = out;
479 }
480 
481 int
482 TerminalWindowSize(long *rows, long *cols)
483 {
484     struct winsize ws;
485 
486     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
487 	*rows = ws.ws_row;
488 	*cols = ws.ws_col;
489 	return 1;
490     }
491     return 0;
492 }
493 
494 int
495 NetClose(int fd)
496 {
497     return close(fd);
498 }
499 
500 
501 void
502 NetNonblockingIO(int fd, int onoff)
503 {
504     ioctl(fd, FIONBIO, (char *)&onoff);
505 }
506 
507 #ifdef TN3270
508 void
509 NetSigIO(int fd, int onoff)
510 {
511     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
512 }
513 
514 void
515 NetSetPgrp(int fd)
516 {
517     int myPid;
518 
519     myPid = getpid();
520     fcntl(fd, F_SETOWN, myPid);
521 }
522 #endif	/*defined(TN3270)*/
523 
524 /*
525  * Various signal handling routines.
526  */
527 
528 /* ARGSUSED */
529 SIG_FUNC_RET
530 deadpeer(int sig)
531 {
532 	setcommandmode();
533 	longjmp(peerdied, -1);
534 }
535 
536 /* ARGSUSED */
537 SIG_FUNC_RET
538 intr(int sig)
539 {
540     if (localchars) {
541 	intp();
542 	return;
543     }
544     setcommandmode();
545     longjmp(toplevel, -1);
546 }
547 
548 /* ARGSUSED */
549 SIG_FUNC_RET
550 intr2(int sig)
551 {
552     if (localchars) {
553 #ifdef	KLUDGELINEMODE
554 	if (kludgelinemode)
555 	    sendbrk();
556 	else
557 #endif
558 	    sendabort();
559 	return;
560     }
561 }
562 
563 /* ARGSUSED */
564 SIG_FUNC_RET
565 susp(int sig)
566 {
567     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
568 	return;
569     if (localchars)
570 	sendsusp();
571 }
572 
573 /* ARGSUSED */
574 SIG_FUNC_RET
575 sendwin(int sig)
576 {
577     if (connected) {
578 	sendnaws();
579     }
580 }
581 
582 /* ARGSUSED */
583 SIG_FUNC_RET
584 ayt(int sig)
585 {
586     if (connected)
587 	sendayt();
588     else
589 	ayt_status();
590 }
591 
592 
593 void
594 sys_telnet_init(void)
595 {
596     (void) signal(SIGINT, intr);
597     (void) signal(SIGQUIT, intr2);
598     (void) signal(SIGPIPE, deadpeer);
599     (void) signal(SIGWINCH, sendwin);
600     (void) signal(SIGTSTP, susp);
601     (void) signal(SIGINFO, ayt);
602 
603     setconnmode(0);
604 
605     NetNonblockingIO(net, 1);
606 
607 #ifdef TN3270
608     if (noasynchnet == 0) {			/* DBX can't handle! */
609 	NetSigIO(net, 1);
610 	NetSetPgrp(net);
611     }
612 #endif	/* defined(TN3270) */
613 
614     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
615 	perror("SetSockOpt");
616     }
617 }
618 
619 /*
620  * Process rings -
621  *
622  *	This routine tries to fill up/empty our various rings.
623  *
624  *	The parameter specifies whether this is a poll operation,
625  *	or a block-until-something-happens operation.
626  *
627  *	The return value is 1 if something happened, 0 if not, < 0 if an
628  *	error occured.
629  */
630 
631 int
632 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
633     int dopoll)		/* If 0, then block until something to do */
634 {
635     struct pollfd set[3];
636     int c;
637 		/* One wants to be a bit careful about setting returnValue
638 		 * to one, since a one implies we did some useful work,
639 		 * and therefore probably won't be called to block next
640 		 * time (TN3270 mode only).
641 		 */
642     int returnValue = 0;
643 
644     set[0].fd = net;
645     set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
646 	(netex ? POLLPRI : 0);
647     set[1].fd = tout;
648     set[1].events = ttyout ? POLLOUT : 0;
649     set[2].fd = tin;
650     set[2].events = ttyin ? POLLIN : 0;
651 
652     if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
653 	if (c == -1) {
654 		    /*
655 		     * we can get EINTR if we are in line mode,
656 		     * and the user does an escape (TSTP), or
657 		     * some other signal generator.
658 		     */
659 	    if (errno == EINTR) {
660 		return 0;
661 	    }
662 #ifdef TN3270
663 		    /*
664 		     * we can get EBADF if we were in transparent
665 		     * mode, and the transcom process died.
666 		    */
667 	    if (errno == EBADF)
668 		return 0;
669 #endif /* defined(TN3270) */
670 		    /* I don't like this, does it ever happen? */
671 	    printf("sleep(5) from telnet, after poll\r\n");
672 	    sleep(5);
673 	}
674 	return 0;
675     }
676 
677     /*
678      * Any urgent data?
679      */
680     if (set[0].revents & POLLPRI) {
681 	SYNCHing = 1;
682 	(void) ttyflush(1);	/* flush already enqueued data */
683     }
684 
685     /*
686      * Something to read from the network...
687      */
688     if (set[0].revents & POLLIN) {
689 	int canread;
690 
691 	canread = ring_empty_consecutive(&netiring);
692 	c = recv(net, (char *)netiring.supply, canread, 0);
693 	if (c < 0 && errno == EWOULDBLOCK) {
694 	    c = 0;
695 	} else if (c <= 0) {
696 	    return -1;
697 	}
698 	if (netdata) {
699 	    Dump('<', netiring.supply, c);
700 	}
701 	if (c)
702 	    ring_supplied(&netiring, c);
703 	returnValue = 1;
704     }
705 
706     /*
707      * Something to read from the tty...
708      */
709     if (set[2].revents & POLLIN) {
710 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
711 	if (c < 0 && errno == EIO)
712 	    c = 0;
713 	if (c < 0 && errno == EWOULDBLOCK) {
714 	    c = 0;
715 	} else {
716 	    if (c < 0) {
717 		return -1;
718 	    }
719 	    if (c == 0) {
720 		/* must be an EOF... */
721 		if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
722 		    *ttyiring.supply = termEofChar;
723 		    c = 1;
724 		} else {
725 		    clienteof = 1;
726 		    shutdown(net, 1);
727 		    return 0;
728 		}
729 	    }
730 	    if (termdata) {
731 		Dump('<', ttyiring.supply, c);
732 	    }
733 	    ring_supplied(&ttyiring, c);
734 	}
735 	returnValue = 1;		/* did something useful */
736     }
737 
738     if (set[0].revents & POLLOUT) {
739 	returnValue |= netflush();
740     }
741     if (set[1].revents & POLLOUT) {
742 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
743     }
744 
745     return returnValue;
746 }
747