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