xref: /minix/usr.bin/telnet/sys_bsd.c (revision fb9c64b2)
1 /*	$NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos 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.33 2012/01/09 16:08:55 christos 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 
72 
73 int
74 	tout,			/* Output file descriptor */
75 	tin,			/* Input file descriptor */
76 	net;
77 
78 struct	termios old_tc = { .c_iflag = 0 };
79 extern struct termios new_tc;
80 
81 # ifndef	TCSANOW
82 #  ifdef TCSETS
83 #   define	TCSANOW		TCSETS
84 #   define	TCSADRAIN	TCSETSW
85 #   define	tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
86 #  else
87 #   ifdef TCSETA
88 #    define	TCSANOW		TCSETA
89 #    define	TCSADRAIN	TCSETAW
90 #    define	tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
91 #   else
92 #    define	TCSANOW		TIOCSETA
93 #    define	TCSADRAIN	TIOCSETAW
94 #    define	tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
95 #   endif
96 #  endif
97 #  define	tcsetattr(f, a, t) ioctl(f, a, (char *)t)
98 #  define	cfgetospeed(ptr)	((ptr)->c_cflag&CBAUD)
99 #  ifdef CIBAUD
100 #   define	cfgetispeed(ptr)	(((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
101 #  else
102 #   define	cfgetispeed(ptr)	cfgetospeed(ptr)
103 #  endif
104 # endif /* TCSANOW */
105 
106 
107 void
108 init_sys(void)
109 {
110     tout = fileno(stdout);
111     tin = fileno(stdin);
112 
113     errno = 0;
114 }
115 
116 
117 int
118 TerminalWrite(char *buf, int  n)
119 {
120     return write(tout, buf, n);
121 }
122 
123 int
124 TerminalRead(unsigned char *buf, int  n)
125 {
126     return read(tin, buf, n);
127 }
128 
129 /*
130  *
131  */
132 
133 int
134 TerminalAutoFlush(void)
135 {
136     return 1;
137 }
138 
139 #ifdef	KLUDGELINEMODE
140 extern int kludgelinemode;
141 #endif
142 /*
143  * TerminalSpecialChars()
144  *
145  * Look at an input character to see if it is a special character
146  * and decide what to do.
147  *
148  * Output:
149  *
150  *	0	Don't add this character.
151  *	1	Do add this character
152  */
153 
154 int
155 TerminalSpecialChars(int c)
156 {
157     if (c == termIntChar) {
158 	intp();
159 	return 0;
160     } else if (c == termQuitChar) {
161 #ifdef	KLUDGELINEMODE
162 	if (kludgelinemode)
163 	    sendbrk();
164 	else
165 #endif
166 	    sendabort();
167 	return 0;
168     } else if (c == termEofChar) {
169 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
170 	    sendeof();
171 	    return 0;
172 	}
173 	return 1;
174     } else if (c == termSuspChar) {
175 	sendsusp();
176 	return(0);
177     } else if (c == termFlushChar) {
178 	xmitAO();		/* Transmit Abort Output */
179 	return 0;
180     } else if (!MODE_LOCAL_CHARS(globalmode)) {
181 	if (c == termKillChar) {
182 	    xmitEL();
183 	    return 0;
184 	} else if (c == termEraseChar) {
185 	    xmitEC();		/* Transmit Erase Character */
186 	    return 0;
187 	}
188     }
189     return 1;
190 }
191 
192 
193 /*
194  * Flush output to the terminal
195  */
196 
197 void
198 TerminalFlushOutput(void)
199 {
200     int com = 0;
201     (void) ioctl(fileno(stdout), TIOCFLUSH, &com);
202 }
203 
204 void
205 TerminalSaveState(void)
206 {
207     tcgetattr(0, &old_tc);
208 
209     new_tc = old_tc;
210 }
211 
212 cc_t *
213 tcval(int func)
214 {
215     switch(func) {
216     case SLC_IP:	return(&termIntChar);
217     case SLC_ABORT:	return(&termQuitChar);
218     case SLC_EOF:	return(&termEofChar);
219     case SLC_EC:	return(&termEraseChar);
220     case SLC_EL:	return(&termKillChar);
221     case SLC_XON:	return(&termStartChar);
222     case SLC_XOFF:	return(&termStopChar);
223     case SLC_FORW1:	return(&termForw1Char);
224     case SLC_FORW2:	return(&termForw2Char);
225     case SLC_AO:	return(&termFlushChar);
226     case SLC_SUSP:	return(&termSuspChar);
227     case SLC_EW:	return(&termWerasChar);
228     case SLC_RP:	return(&termRprntChar);
229     case SLC_LNEXT:	return(&termLiteralNextChar);
230     case SLC_AYT:	return(&termAytChar);
231 
232     case SLC_SYNCH:
233     case SLC_BRK:
234     case SLC_EOR:
235     default:
236 	return((cc_t *)0);
237     }
238 }
239 
240 void
241 TerminalDefaultChars(void)
242 {
243     memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
244 }
245 
246 #ifdef notdef
247 void
248 TerminalRestoreState(void)
249 {
250 }
251 #endif
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(int f)
278 {
279     static int prevmode = 0;
280     struct termios tmp_tc;
281     int onoff;
282     int old;
283     cc_t esc;
284 
285     globalmode = f&~MODE_FORCE;
286     if (prevmode == f)
287 	return;
288 
289     /*
290      * Write any outstanding data before switching modes
291      * ttyflush() returns 0 only when there is no more data
292      * left to write out, it returns -1 if it couldn't do
293      * anything at all, otherwise it returns 1 + the number
294      * of characters left to write.
295 #ifndef	USE_TERMIO
296      * We would really like to ask the kernel to wait for the output
297      * to drain, like we can do with the TCSADRAIN, but we don't have
298      * that option.  The only ioctl that waits for the output to
299      * drain, TIOCSETP, also flushes the input queue, which is NOT
300      * what we want (TIOCSETP is like TCSADFLUSH).
301 #endif
302      */
303     old = ttyflush(SYNCHing|flushout);
304     if (old < 0 || old > 1) {
305 	tcgetattr(tin, &tmp_tc);
306 	do {
307 	    /*
308 	     * Wait for data to drain, then flush again.
309 	     */
310 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
311 	    old = ttyflush(SYNCHing|flushout);
312 	} while (old < 0 || old > 1);
313     }
314 
315     old = prevmode;
316     prevmode = f&~MODE_FORCE;
317     tmp_tc = new_tc;
318 
319     if (f&MODE_ECHO) {
320 	tmp_tc.c_lflag |= ECHO;
321 	tmp_tc.c_oflag |= ONLCR;
322 	if (crlf)
323 		tmp_tc.c_iflag |= ICRNL;
324     } else {
325 	tmp_tc.c_lflag &= ~ECHO;
326 	tmp_tc.c_oflag &= ~ONLCR;
327 # ifdef notdef
328 	if (crlf)
329 		tmp_tc.c_iflag &= ~ICRNL;
330 # endif
331     }
332 
333     if ((f&MODE_FLOW) == 0) {
334 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
335     } else {
336 	if (restartany < 0) {
337 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
338 	} else if (restartany > 0) {
339 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
340 	} else {
341 		tmp_tc.c_iflag |= IXOFF|IXON;
342 		tmp_tc.c_iflag &= ~IXANY;
343 	}
344     }
345 
346     if ((f&MODE_TRAPSIG) == 0) {
347 	tmp_tc.c_lflag &= ~ISIG;
348 	localchars = 0;
349     } else {
350 	tmp_tc.c_lflag |= ISIG;
351 	localchars = 1;
352     }
353 
354     if (f&MODE_EDIT) {
355 	tmp_tc.c_lflag |= ICANON;
356     } else {
357 	tmp_tc.c_lflag &= ~ICANON;
358 	tmp_tc.c_iflag &= ~ICRNL;
359 	tmp_tc.c_cc[VMIN] = 1;
360 	tmp_tc.c_cc[VTIME] = 0;
361     }
362 
363     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
364 	tmp_tc.c_lflag &= ~IEXTEN;
365     }
366 
367     if (f&MODE_SOFT_TAB) {
368 # ifdef	OXTABS
369 	tmp_tc.c_oflag |= OXTABS;
370 # endif
371 # ifdef	TABDLY
372 	tmp_tc.c_oflag &= ~TABDLY;
373 	tmp_tc.c_oflag |= TAB3;
374 # endif
375     } else {
376 # ifdef	OXTABS
377 	tmp_tc.c_oflag &= ~OXTABS;
378 # endif
379 # ifdef	TABDLY
380 	tmp_tc.c_oflag &= ~TABDLY;
381 # endif
382     }
383 
384     if (f&MODE_LIT_ECHO) {
385 # ifdef	ECHOCTL
386 	tmp_tc.c_lflag &= ~ECHOCTL;
387 # endif
388     } else {
389 # ifdef	ECHOCTL
390 	tmp_tc.c_lflag |= ECHOCTL;
391 # endif
392     }
393 
394     if (f == -1) {
395 	onoff = 0;
396     } else {
397 	if (f & MODE_INBIN)
398 		tmp_tc.c_iflag &= ~ISTRIP;
399 	else
400 		tmp_tc.c_iflag |= ISTRIP;
401 	if (f & MODE_OUTBIN) {
402 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
403 		tmp_tc.c_cflag |= CS8;
404 		tmp_tc.c_oflag &= ~OPOST;
405 	} else {
406 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
407 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
408 		tmp_tc.c_oflag |= OPOST;
409 	}
410 	onoff = 1;
411     }
412 
413     if (f != -1) {
414 	(void) signal(SIGTSTP, susp);
415 	(void) signal(SIGINFO, ayt);
416 #if	defined(USE_TERMIO) && defined(NOKERNINFO)
417 	tmp_tc.c_lflag |= NOKERNINFO;
418 #endif
419 	/*
420 	 * We don't want to process ^Y here.  It's just another
421 	 * character that we'll pass on to the back end.  It has
422 	 * to process it because it will be processed when the
423 	 * user attempts to read it, not when we send it.
424 	 */
425 # ifdef	VDSUSP
426 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
427 # endif
428 	/*
429 	 * If the VEOL character is already set, then use VEOL2,
430 	 * otherwise use VEOL.
431 	 */
432 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
433 	if ((tmp_tc.c_cc[VEOL] != esc)
434 # ifdef	VEOL2
435 	    && (tmp_tc.c_cc[VEOL2] != esc)
436 # endif
437 	    ) {
438 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
439 		    tmp_tc.c_cc[VEOL] = esc;
440 # ifdef	VEOL2
441 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
442 		    tmp_tc.c_cc[VEOL2] = esc;
443 # endif
444 	}
445     } else {
446 	(void) signal(SIGINFO, (void (*)(int)) ayt_status);
447 	(void) signal(SIGTSTP, SIG_DFL);
448 	(void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
449 	tmp_tc = old_tc;
450     }
451     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
452 	tcsetattr(tin, TCSANOW, &tmp_tc);
453 
454     ioctl(tin, FIONBIO, (char *)&onoff);
455     ioctl(tout, FIONBIO, (char *)&onoff);
456 #if	defined(TN3270)
457     if (noasynchtty == 0) {
458 	ioctl(tin, FIOASYNC, (char *)&onoff);
459     }
460 #endif	/* defined(TN3270) */
461 
462 }
463 
464 void
465 TerminalSpeeds(long *ispeed, long *ospeed)
466 {
467     long in, out;
468 
469     out = cfgetospeed(&old_tc);
470     in = cfgetispeed(&old_tc);
471     if (in == 0)
472 	in = out;
473 
474 	*ispeed = in;
475 	*ospeed = out;
476 }
477 
478 int
479 TerminalWindowSize(long *rows, long *cols)
480 {
481     struct winsize ws;
482 
483     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
484 	*rows = ws.ws_row;
485 	*cols = ws.ws_col;
486 	return 1;
487     }
488     return 0;
489 }
490 
491 int
492 NetClose(int fd)
493 {
494     return close(fd);
495 }
496 
497 
498 void
499 NetNonblockingIO(int fd, int onoff)
500 {
501     ioctl(fd, FIONBIO, (char *)&onoff);
502 }
503 
504 #ifdef TN3270
505 void
506 NetSigIO(int fd, int onoff)
507 {
508     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
509 }
510 
511 void
512 NetSetPgrp(int fd)
513 {
514     int myPid;
515 
516     myPid = getpid();
517     fcntl(fd, F_SETOWN, myPid);
518 }
519 #endif	/*defined(TN3270)*/
520 
521 /*
522  * Various signal handling routines.
523  */
524 
525 /* ARGSUSED */
526 SIG_FUNC_RET
527 intr(int sig)
528 {
529     if (localchars) {
530 	intp();
531 	return;
532     }
533     setcommandmode();
534     longjmp(toplevel, -1);
535 }
536 
537 /* ARGSUSED */
538 SIG_FUNC_RET
539 intr2(int sig)
540 {
541     if (localchars) {
542 #ifdef	KLUDGELINEMODE
543 	if (kludgelinemode)
544 	    sendbrk();
545 	else
546 #endif
547 	    sendabort();
548 	return;
549     }
550 }
551 
552 /* ARGSUSED */
553 SIG_FUNC_RET
554 susp(int sig)
555 {
556     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
557 	return;
558     if (localchars)
559 	sendsusp();
560 }
561 
562 /* ARGSUSED */
563 SIG_FUNC_RET
564 sendwin(int sig)
565 {
566     if (connected) {
567 	sendnaws();
568     }
569 }
570 
571 /* ARGSUSED */
572 SIG_FUNC_RET
573 ayt(int sig)
574 {
575     if (connected)
576 	sendayt();
577     else
578 	ayt_status();
579 }
580 
581 
582 void
583 sys_telnet_init(void)
584 {
585     (void) signal(SIGINT, intr);
586     (void) signal(SIGQUIT, intr2);
587     (void) signal(SIGPIPE, SIG_IGN);
588     (void) signal(SIGWINCH, sendwin);
589     (void) signal(SIGTSTP, susp);
590     (void) signal(SIGINFO, ayt);
591 
592     setconnmode(0);
593 
594     NetNonblockingIO(net, 1);
595 
596 #ifdef TN3270
597     if (noasynchnet == 0) {			/* DBX can't handle! */
598 	NetSigIO(net, 1);
599 	NetSetPgrp(net);
600     }
601 #endif	/* defined(TN3270) */
602 
603     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
604 	perror("SetSockOpt");
605     }
606 }
607 
608 /*
609  * Process rings -
610  *
611  *	This routine tries to fill up/empty our various rings.
612  *
613  *	The parameter specifies whether this is a poll operation,
614  *	or a block-until-something-happens operation.
615  *
616  *	The return value is 1 if something happened, 0 if not, < 0 if an
617  *	error occurred.
618  */
619 
620 int
621 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
622     int dopoll)		/* If 0, then block until something to do */
623 {
624     struct pollfd set[3];
625     int c;
626 		/* One wants to be a bit careful about setting returnValue
627 		 * to one, since a one implies we did some useful work,
628 		 * and therefore probably won't be called to block next
629 		 * time (TN3270 mode only).
630 		 */
631     int returnValue = 0;
632 
633     set[0].fd = net;
634     set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
635 	(netex ? POLLPRI : 0);
636     set[1].fd = tout;
637     set[1].events = ttyout ? POLLOUT : 0;
638     set[2].fd = tin;
639     set[2].events = ttyin ? POLLIN : 0;
640 
641     if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
642 	if (c == -1) {
643 		    /*
644 		     * we can get EINTR if we are in line mode,
645 		     * and the user does an escape (TSTP), or
646 		     * some other signal generator.
647 		     */
648 	    if (errno == EINTR) {
649 		return 0;
650 	    }
651 #ifdef TN3270
652 		    /*
653 		     * we can get EBADF if we were in transparent
654 		     * mode, and the transcom process died.
655 		    */
656 	    if (errno == EBADF)
657 		return 0;
658 #endif /* defined(TN3270) */
659 		    /* I don't like this, does it ever happen? */
660 	    printf("sleep(5) from telnet, after poll\r\n");
661 	    sleep(5);
662 	}
663 	return 0;
664     }
665 
666     /*
667      * Any urgent data?
668      */
669     if (set[0].revents & POLLPRI) {
670 	SYNCHing = 1;
671 	(void) ttyflush(1);	/* flush already enqueued data */
672     }
673 
674     /*
675      * Something to read from the network...
676      */
677     if (set[0].revents & POLLIN) {
678 	int canread;
679 
680 	canread = ring_empty_consecutive(&netiring);
681 	c = recv(net, (char *)netiring.supply, canread, 0);
682 	if (c < 0 && errno == EWOULDBLOCK) {
683 	    c = 0;
684 	} else if (c <= 0) {
685 	    return -1;
686 	}
687 	if (netdata) {
688 	    Dump('<', netiring.supply, c);
689 	}
690 	if (c)
691 	    ring_supplied(&netiring, c);
692 	returnValue = 1;
693     }
694 
695     /*
696      * Something to read from the tty...
697      */
698     if (set[2].revents & POLLIN) {
699 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
700 	if (c < 0 && errno == EIO)
701 	    c = 0;
702 	if (c < 0 && errno == EWOULDBLOCK) {
703 	    c = 0;
704 	} else {
705 	    if (c < 0) {
706 		return -1;
707 	    }
708 	    if (c == 0) {
709 		/* must be an EOF... */
710 		if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
711 		    *ttyiring.supply = termEofChar;
712 		    c = 1;
713 		} else {
714 		    clienteof = 1;
715 		    shutdown(net, 1);
716 		    return 0;
717 		}
718 	    }
719 	    if (termdata) {
720 		Dump('<', ttyiring.supply, c);
721 	    }
722 	    ring_supplied(&ttyiring, c);
723 	}
724 	returnValue = 1;		/* did something useful */
725     }
726 
727     if (set[0].revents & POLLOUT) {
728 	returnValue |= netflush();
729     }
730 
731     if (set[1].revents & (POLLHUP|POLLNVAL))
732 	return(-1);
733 
734     if (set[1].revents & POLLOUT) {
735 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
736     }
737 
738     return returnValue;
739 }
740