sys_bsd.c revision 1.30 1 /* $NetBSD: sys_bsd.c,v 1.30 2004/02/24 15:12:53 wiz 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.30 2004/02/24 15:12:53 wiz 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 /*
526 * Various signal handling routines.
527 */
528
529 /* ARGSUSED */
530 SIG_FUNC_RET
531 deadpeer(int sig)
532 {
533 setcommandmode();
534 longjmp(peerdied, -1);
535 }
536
537 /* ARGSUSED */
538 SIG_FUNC_RET
539 intr(int sig)
540 {
541 if (localchars) {
542 intp();
543 return;
544 }
545 setcommandmode();
546 longjmp(toplevel, -1);
547 }
548
549 /* ARGSUSED */
550 SIG_FUNC_RET
551 intr2(int sig)
552 {
553 if (localchars) {
554 #ifdef KLUDGELINEMODE
555 if (kludgelinemode)
556 sendbrk();
557 else
558 #endif
559 sendabort();
560 return;
561 }
562 }
563
564 /* ARGSUSED */
565 SIG_FUNC_RET
566 susp(int sig)
567 {
568 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
569 return;
570 if (localchars)
571 sendsusp();
572 }
573
574 /* ARGSUSED */
575 SIG_FUNC_RET
576 sendwin(int sig)
577 {
578 if (connected) {
579 sendnaws();
580 }
581 }
582
583 /* ARGSUSED */
584 SIG_FUNC_RET
585 ayt(int sig)
586 {
587 if (connected)
588 sendayt();
589 else
590 ayt_status();
591 }
592
593
594 void
596 sys_telnet_init(void)
597 {
598 (void) signal(SIGINT, intr);
599 (void) signal(SIGQUIT, intr2);
600 (void) signal(SIGPIPE, deadpeer);
601 (void) signal(SIGWINCH, sendwin);
602 (void) signal(SIGTSTP, susp);
603 (void) signal(SIGINFO, ayt);
604
605 setconnmode(0);
606
607 NetNonblockingIO(net, 1);
608
609 #ifdef TN3270
610 if (noasynchnet == 0) { /* DBX can't handle! */
611 NetSigIO(net, 1);
612 NetSetPgrp(net);
613 }
614 #endif /* defined(TN3270) */
615
616 if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
617 perror("SetSockOpt");
618 }
619 }
620
621 /*
622 * Process rings -
623 *
624 * This routine tries to fill up/empty our various rings.
625 *
626 * The parameter specifies whether this is a poll operation,
627 * or a block-until-something-happens operation.
628 *
629 * The return value is 1 if something happened, 0 if not, < 0 if an
630 * error occurred.
631 */
632
633 int
634 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
635 int dopoll) /* If 0, then block until something to do */
636 {
637 struct pollfd set[3];
638 int c;
639 /* One wants to be a bit careful about setting returnValue
640 * to one, since a one implies we did some useful work,
641 * and therefore probably won't be called to block next
642 * time (TN3270 mode only).
643 */
644 int returnValue = 0;
645
646 set[0].fd = net;
647 set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
648 (netex ? POLLPRI : 0);
649 set[1].fd = tout;
650 set[1].events = ttyout ? POLLOUT : 0;
651 set[2].fd = tin;
652 set[2].events = ttyin ? POLLIN : 0;
653
654 if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
655 if (c == -1) {
656 /*
657 * we can get EINTR if we are in line mode,
658 * and the user does an escape (TSTP), or
659 * some other signal generator.
660 */
661 if (errno == EINTR) {
662 return 0;
663 }
664 #ifdef TN3270
665 /*
666 * we can get EBADF if we were in transparent
667 * mode, and the transcom process died.
668 */
669 if (errno == EBADF)
670 return 0;
671 #endif /* defined(TN3270) */
672 /* I don't like this, does it ever happen? */
673 printf("sleep(5) from telnet, after poll\r\n");
674 sleep(5);
675 }
676 return 0;
677 }
678
679 /*
680 * Any urgent data?
681 */
682 if (set[0].revents & POLLPRI) {
683 SYNCHing = 1;
684 (void) ttyflush(1); /* flush already enqueued data */
685 }
686
687 /*
688 * Something to read from the network...
689 */
690 if (set[0].revents & POLLIN) {
691 int canread;
692
693 canread = ring_empty_consecutive(&netiring);
694 c = recv(net, (char *)netiring.supply, canread, 0);
695 if (c < 0 && errno == EWOULDBLOCK) {
696 c = 0;
697 } else if (c <= 0) {
698 return -1;
699 }
700 if (netdata) {
701 Dump('<', netiring.supply, c);
702 }
703 if (c)
704 ring_supplied(&netiring, c);
705 returnValue = 1;
706 }
707
708 /*
709 * Something to read from the tty...
710 */
711 if (set[2].revents & POLLIN) {
712 c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
713 if (c < 0 && errno == EIO)
714 c = 0;
715 if (c < 0 && errno == EWOULDBLOCK) {
716 c = 0;
717 } else {
718 if (c < 0) {
719 return -1;
720 }
721 if (c == 0) {
722 /* must be an EOF... */
723 if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
724 *ttyiring.supply = termEofChar;
725 c = 1;
726 } else {
727 clienteof = 1;
728 shutdown(net, 1);
729 return 0;
730 }
731 }
732 if (termdata) {
733 Dump('<', ttyiring.supply, c);
734 }
735 ring_supplied(&ttyiring, c);
736 }
737 returnValue = 1; /* did something useful */
738 }
739
740 if (set[0].revents & POLLOUT) {
741 returnValue |= netflush();
742 }
743 if (set[1].revents & POLLOUT) {
744 returnValue |= (ttyflush(SYNCHing|flushout) > 0);
745 }
746
747 return returnValue;
748 }
749