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
init_sys(void)108 init_sys(void)
109 {
110 tout = fileno(stdout);
111 tin = fileno(stdin);
112
113 errno = 0;
114 }
115
116
117 int
TerminalWrite(char * buf,int n)118 TerminalWrite(char *buf, int n)
119 {
120 return write(tout, buf, n);
121 }
122
123 int
TerminalRead(unsigned char * buf,int n)124 TerminalRead(unsigned char *buf, int n)
125 {
126 return read(tin, buf, n);
127 }
128
129 /*
130 *
131 */
132
133 int
TerminalAutoFlush(void)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
TerminalSpecialChars(int c)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
TerminalFlushOutput(void)198 TerminalFlushOutput(void)
199 {
200 int com = 0;
201 (void) ioctl(fileno(stdout), TIOCFLUSH, &com);
202 }
203
204 void
TerminalSaveState(void)205 TerminalSaveState(void)
206 {
207 tcgetattr(0, &old_tc);
208
209 new_tc = old_tc;
210 }
211
212 cc_t *
tcval(int func)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
TerminalDefaultChars(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
TerminalRestoreState(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
TerminalNewMode(int f)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
TerminalSpeeds(long * ispeed,long * ospeed)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
TerminalWindowSize(long * rows,long * cols)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
NetClose(int fd)492 NetClose(int fd)
493 {
494 return close(fd);
495 }
496
497
498 void
NetNonblockingIO(int fd,int onoff)499 NetNonblockingIO(int fd, int onoff)
500 {
501 ioctl(fd, FIONBIO, (char *)&onoff);
502 }
503
504 #ifdef TN3270
505 void
NetSigIO(int fd,int onoff)506 NetSigIO(int fd, int onoff)
507 {
508 ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */
509 }
510
511 void
NetSetPgrp(int fd)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
intr(int sig)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
intr2(int sig)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
susp(int sig)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
sendwin(int sig)564 sendwin(int sig)
565 {
566 if (connected) {
567 sendnaws();
568 }
569 }
570
571 /* ARGSUSED */
572 SIG_FUNC_RET
ayt(int sig)573 ayt(int sig)
574 {
575 if (connected)
576 sendayt();
577 else
578 ayt_status();
579 }
580
581
582 void
sys_telnet_init(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
process_rings(int netin,int netout,int netex,int ttyin,int ttyout,int dopoll)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