sys_bsd.c revision 1.31 1 /* $NetBSD: sys_bsd.c,v 1.31 2004/03/20 23:26:05 heas 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.31 2004/03/20 23:26:05 heas 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 } while (old < 0 || old > 1);
314 }
315
316 old = prevmode;
317 prevmode = f&~MODE_FORCE;
318 tmp_tc = new_tc;
319
320 if (f&MODE_ECHO) {
321 tmp_tc.c_lflag |= ECHO;
322 tmp_tc.c_oflag |= ONLCR;
323 if (crlf)
324 tmp_tc.c_iflag |= ICRNL;
325 } else {
326 tmp_tc.c_lflag &= ~ECHO;
327 tmp_tc.c_oflag &= ~ONLCR;
328 # ifdef notdef
329 if (crlf)
330 tmp_tc.c_iflag &= ~ICRNL;
331 # endif
332 }
333
334 if ((f&MODE_FLOW) == 0) {
335 tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */
336 } else {
337 if (restartany < 0) {
338 tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */
339 } else if (restartany > 0) {
340 tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
341 } else {
342 tmp_tc.c_iflag |= IXOFF|IXON;
343 tmp_tc.c_iflag &= ~IXANY;
344 }
345 }
346
347 if ((f&MODE_TRAPSIG) == 0) {
348 tmp_tc.c_lflag &= ~ISIG;
349 localchars = 0;
350 } else {
351 tmp_tc.c_lflag |= ISIG;
352 localchars = 1;
353 }
354
355 if (f&MODE_EDIT) {
356 tmp_tc.c_lflag |= ICANON;
357 } else {
358 tmp_tc.c_lflag &= ~ICANON;
359 tmp_tc.c_iflag &= ~ICRNL;
360 tmp_tc.c_cc[VMIN] = 1;
361 tmp_tc.c_cc[VTIME] = 0;
362 }
363
364 if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
365 tmp_tc.c_lflag &= ~IEXTEN;
366 }
367
368 if (f&MODE_SOFT_TAB) {
369 # ifdef OXTABS
370 tmp_tc.c_oflag |= OXTABS;
371 # endif
372 # ifdef TABDLY
373 tmp_tc.c_oflag &= ~TABDLY;
374 tmp_tc.c_oflag |= TAB3;
375 # endif
376 } else {
377 # ifdef OXTABS
378 tmp_tc.c_oflag &= ~OXTABS;
379 # endif
380 # ifdef TABDLY
381 tmp_tc.c_oflag &= ~TABDLY;
382 # endif
383 }
384
385 if (f&MODE_LIT_ECHO) {
386 # ifdef ECHOCTL
387 tmp_tc.c_lflag &= ~ECHOCTL;
388 # endif
389 } else {
390 # ifdef ECHOCTL
391 tmp_tc.c_lflag |= ECHOCTL;
392 # endif
393 }
394
395 if (f == -1) {
396 onoff = 0;
397 } else {
398 if (f & MODE_INBIN)
399 tmp_tc.c_iflag &= ~ISTRIP;
400 else
401 tmp_tc.c_iflag |= ISTRIP;
402 if (f & MODE_OUTBIN) {
403 tmp_tc.c_cflag &= ~(CSIZE|PARENB);
404 tmp_tc.c_cflag |= CS8;
405 tmp_tc.c_oflag &= ~OPOST;
406 } else {
407 tmp_tc.c_cflag &= ~(CSIZE|PARENB);
408 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
409 tmp_tc.c_oflag |= OPOST;
410 }
411 onoff = 1;
412 }
413
414 if (f != -1) {
415 (void) signal(SIGTSTP, susp);
416 (void) signal(SIGINFO, ayt);
417 #if defined(USE_TERMIO) && defined(NOKERNINFO)
418 tmp_tc.c_lflag |= NOKERNINFO;
419 #endif
420 /*
421 * We don't want to process ^Y here. It's just another
422 * character that we'll pass on to the back end. It has
423 * to process it because it will be processed when the
424 * user attempts to read it, not when we send it.
425 */
426 # ifdef VDSUSP
427 tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
428 # endif
429 /*
430 * If the VEOL character is already set, then use VEOL2,
431 * otherwise use VEOL.
432 */
433 esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
434 if ((tmp_tc.c_cc[VEOL] != esc)
435 # ifdef VEOL2
436 && (tmp_tc.c_cc[VEOL2] != esc)
437 # endif
438 ) {
439 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
440 tmp_tc.c_cc[VEOL] = esc;
441 # ifdef VEOL2
442 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
443 tmp_tc.c_cc[VEOL2] = esc;
444 # endif
445 }
446 } else {
447 (void) signal(SIGINFO, (void (*)(int)) ayt_status);
448 (void) signal(SIGTSTP, SIG_DFL);
449 (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
450 tmp_tc = old_tc;
451 }
452 if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
453 tcsetattr(tin, TCSANOW, &tmp_tc);
454
455 ioctl(tin, FIONBIO, (char *)&onoff);
456 ioctl(tout, FIONBIO, (char *)&onoff);
457 #if defined(TN3270)
458 if (noasynchtty == 0) {
459 ioctl(tin, FIOASYNC, (char *)&onoff);
460 }
461 #endif /* defined(TN3270) */
462
463 }
464
465 void
466 TerminalSpeeds(long *ispeed, long *ospeed)
467 {
468 long in, out;
469
470 out = cfgetospeed(&old_tc);
471 in = cfgetispeed(&old_tc);
472 if (in == 0)
473 in = out;
474
475 *ispeed = in;
476 *ospeed = out;
477 }
478
479 int
480 TerminalWindowSize(long *rows, long *cols)
481 {
482 struct winsize ws;
483
484 if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
485 *rows = ws.ws_row;
486 *cols = ws.ws_col;
487 return 1;
488 }
489 return 0;
490 }
491
492 int
493 NetClose(int fd)
494 {
495 return close(fd);
496 }
497
498
499 void
500 NetNonblockingIO(int fd, int onoff)
501 {
502 ioctl(fd, FIONBIO, (char *)&onoff);
503 }
504
505 #ifdef TN3270
506 void
507 NetSigIO(int fd, int onoff)
508 {
509 ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */
510 }
511
512 void
513 NetSetPgrp(int fd)
514 {
515 int myPid;
516
517 myPid = getpid();
518 fcntl(fd, F_SETOWN, myPid);
519 }
520 #endif /*defined(TN3270)*/
521
522 /*
524 * Various signal handling routines.
525 */
526
527 /* ARGSUSED */
528 SIG_FUNC_RET
529 intr(int sig)
530 {
531 if (localchars) {
532 intp();
533 return;
534 }
535 setcommandmode();
536 longjmp(toplevel, -1);
537 }
538
539 /* ARGSUSED */
540 SIG_FUNC_RET
541 intr2(int sig)
542 {
543 if (localchars) {
544 #ifdef KLUDGELINEMODE
545 if (kludgelinemode)
546 sendbrk();
547 else
548 #endif
549 sendabort();
550 return;
551 }
552 }
553
554 /* ARGSUSED */
555 SIG_FUNC_RET
556 susp(int sig)
557 {
558 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
559 return;
560 if (localchars)
561 sendsusp();
562 }
563
564 /* ARGSUSED */
565 SIG_FUNC_RET
566 sendwin(int sig)
567 {
568 if (connected) {
569 sendnaws();
570 }
571 }
572
573 /* ARGSUSED */
574 SIG_FUNC_RET
575 ayt(int sig)
576 {
577 if (connected)
578 sendayt();
579 else
580 ayt_status();
581 }
582
583
584 void
586 sys_telnet_init(void)
587 {
588 (void) signal(SIGINT, intr);
589 (void) signal(SIGQUIT, intr2);
590 (void) signal(SIGPIPE, SIG_IGN);
591 (void) signal(SIGWINCH, sendwin);
592 (void) signal(SIGTSTP, susp);
593 (void) signal(SIGINFO, ayt);
594
595 setconnmode(0);
596
597 NetNonblockingIO(net, 1);
598
599 #ifdef TN3270
600 if (noasynchnet == 0) { /* DBX can't handle! */
601 NetSigIO(net, 1);
602 NetSetPgrp(net);
603 }
604 #endif /* defined(TN3270) */
605
606 if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
607 perror("SetSockOpt");
608 }
609 }
610
611 /*
612 * Process rings -
613 *
614 * This routine tries to fill up/empty our various rings.
615 *
616 * The parameter specifies whether this is a poll operation,
617 * or a block-until-something-happens operation.
618 *
619 * The return value is 1 if something happened, 0 if not, < 0 if an
620 * error occurred.
621 */
622
623 int
624 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
625 int dopoll) /* If 0, then block until something to do */
626 {
627 struct pollfd set[3];
628 int c;
629 /* One wants to be a bit careful about setting returnValue
630 * to one, since a one implies we did some useful work,
631 * and therefore probably won't be called to block next
632 * time (TN3270 mode only).
633 */
634 int returnValue = 0;
635
636 set[0].fd = net;
637 set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
638 (netex ? POLLPRI : 0);
639 set[1].fd = tout;
640 set[1].events = ttyout ? POLLOUT : 0;
641 set[2].fd = tin;
642 set[2].events = ttyin ? POLLIN : 0;
643
644 if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
645 if (c == -1) {
646 /*
647 * we can get EINTR if we are in line mode,
648 * and the user does an escape (TSTP), or
649 * some other signal generator.
650 */
651 if (errno == EINTR) {
652 return 0;
653 }
654 #ifdef TN3270
655 /*
656 * we can get EBADF if we were in transparent
657 * mode, and the transcom process died.
658 */
659 if (errno == EBADF)
660 return 0;
661 #endif /* defined(TN3270) */
662 /* I don't like this, does it ever happen? */
663 printf("sleep(5) from telnet, after poll\r\n");
664 sleep(5);
665 }
666 return 0;
667 }
668
669 /*
670 * Any urgent data?
671 */
672 if (set[0].revents & POLLPRI) {
673 SYNCHing = 1;
674 (void) ttyflush(1); /* flush already enqueued data */
675 }
676
677 /*
678 * Something to read from the network...
679 */
680 if (set[0].revents & POLLIN) {
681 int canread;
682
683 canread = ring_empty_consecutive(&netiring);
684 c = recv(net, (char *)netiring.supply, canread, 0);
685 if (c < 0 && errno == EWOULDBLOCK) {
686 c = 0;
687 } else if (c <= 0) {
688 return -1;
689 }
690 if (netdata) {
691 Dump('<', netiring.supply, c);
692 }
693 if (c)
694 ring_supplied(&netiring, c);
695 returnValue = 1;
696 }
697
698 /*
699 * Something to read from the tty...
700 */
701 if (set[2].revents & POLLIN) {
702 c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
703 if (c < 0 && errno == EIO)
704 c = 0;
705 if (c < 0 && errno == EWOULDBLOCK) {
706 c = 0;
707 } else {
708 if (c < 0) {
709 return -1;
710 }
711 if (c == 0) {
712 /* must be an EOF... */
713 if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
714 *ttyiring.supply = termEofChar;
715 c = 1;
716 } else {
717 clienteof = 1;
718 shutdown(net, 1);
719 return 0;
720 }
721 }
722 if (termdata) {
723 Dump('<', ttyiring.supply, c);
724 }
725 ring_supplied(&ttyiring, c);
726 }
727 returnValue = 1; /* did something useful */
728 }
729
730 if (set[0].revents & POLLOUT) {
731 returnValue |= netflush();
732 }
733
734 if (set[1].revents & (POLLHUP|POLLNVAL))
735 return(-1);
736
737 if (set[1].revents & POLLOUT) {
738 returnValue |= (ttyflush(SYNCHing|flushout) > 0);
739 }
740
741 return returnValue;
742 }
743