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