date and time created 88/01/07 11:10:47 by bostic
[unix-history] / usr / src / usr.bin / telnet / sys_bsd.c
CommitLineData
e767b7ab
GM
1/*
2 * The following routines try to encapsulate what is system dependent
3 * (at least between 4.x and dos) which is used in telnet.c.
4 */
5
6#if defined(unix)
7
8#include <sys/ioctl.h>
115a5494 9#include <sys/types.h>
e767b7ab 10#include <sys/time.h>
b307f09e 11#include <sys/socket.h>
e767b7ab 12#include <signal.h>
b307f09e 13#include <errno.h>
e767b7ab 14
115a5494
GM
15#include "ring.h"
16
807a3a7d
GM
17#include "fdset.h"
18
e767b7ab
GM
19#include "defines.h"
20#include "externs.h"
21#include "types.h"
22
23int
b307f09e
GM
24 tout, /* Output file descriptor */
25 tin, /* Input file descriptor */
26 net,
e767b7ab
GM
27 HaveInput; /* There is input available to scan */
28
29#if defined(TN3270)
30static char tline[200];
31char *transcom = 0; /* transparent mode command (default: none) */
32#endif /* defined(TN3270) */
33
34static struct tchars otc = { 0 }, ntc = { 0 };
35static struct ltchars oltc = { 0 }, nltc = { 0 };
36static struct sgttyb ottyb = { 0 }, nttyb = { 0 };
37
b307f09e
GM
38static fd_set ibits, obits, xbits;
39
40
41init_sys()
42{
43 tout = fileno(stdout);
44 tin = fileno(stdin);
45 FD_ZERO(&ibits);
46 FD_ZERO(&obits);
47 FD_ZERO(&xbits);
48
49 errno = 0;
50}
51
e767b7ab
GM
52
53TerminalWrite(fd, buf, n)
54int fd;
55char *buf;
56int n;
57{
58 return write(fd, buf, n);
59}
60
61TerminalRead(fd, buf, n)
62int fd;
63char *buf;
64int n;
65{
66 return read(fd, buf, n);
67}
68
69/*
70 *
71 */
72
73int
310e7d0b 74TerminalAutoFlush()
e767b7ab
GM
75{
76#if defined(LNOFLSH)
77 int flush;
78
79 ioctl(0, TIOCLGET, (char *)&flush);
80 return !(flush&LNOFLSH); /* if LNOFLSH, no autoflush */
81#else /* LNOFLSH */
82 return 1;
83#endif /* LNOFLSH */
84}
85
86/*
87 * TerminalSpecialChars()
88 *
89 * Look at an input character to see if it is a special character
90 * and decide what to do.
91 *
92 * Output:
93 *
94 * 0 Don't add this character.
95 * 1 Do add this character
96 */
97
98int
310e7d0b 99TerminalSpecialChars(c)
e767b7ab
GM
100int c;
101{
310e7d0b 102 void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
e767b7ab
GM
103
104 if (c == ntc.t_intrc) {
105 intp();
106 return 0;
107 } else if (c == ntc.t_quitc) {
108 sendbrk();
109 return 0;
110 } else if (c == nltc.t_flushc) {
111 xmitAO(); /* Transmit Abort Output */
112 return 0;
113 } else if (!MODE_LOCAL_CHARS(globalmode)) {
114 if (c == nttyb.sg_kill) {
115 xmitEL();
116 return 0;
117 } else if (c == nttyb.sg_erase) {
118 xmitEC(); /* Transmit Erase Character */
119 return 0;
120 }
121 }
122 return 1;
123}
124
125
126/*
127 * Flush output to the terminal
128 */
129
130void
310e7d0b 131TerminalFlushOutput()
e767b7ab
GM
132{
133 (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
134}
135
136void
310e7d0b 137TerminalSaveState()
e767b7ab
GM
138{
139 ioctl(0, TIOCGETP, (char *)&ottyb);
140 ioctl(0, TIOCGETC, (char *)&otc);
141 ioctl(0, TIOCGLTC, (char *)&oltc);
142
143 ntc = otc;
144 nltc = oltc;
145 nttyb = ottyb;
0e29050b
GM
146
147 termEofChar = ntc.t_eofc;
148 termEraseChar = nttyb.sg_erase;
149 termFlushChar = nltc.t_flushc;
150 termIntChar = ntc.t_intrc;
151 termKillChar = nttyb.sg_kill;
152 termQuitChar = ntc.t_quitc;
e767b7ab
GM
153}
154
155void
310e7d0b 156TerminalRestoreState()
e767b7ab
GM
157{
158}
159
160/*
161 * TerminalNewMode - set up terminal to a specific mode.
162 */
163
164
165void
310e7d0b 166TerminalNewMode(fd_in, fd_out, f)
e767b7ab
GM
167int fd_in, fd_out; /* File descriptor */
168register int f;
169{
170 static int prevmode = 0;
171 struct tchars *tc;
172 struct tchars tc3;
173 struct ltchars *ltc;
174 struct sgttyb sb;
175 int onoff;
176 int old;
177 struct tchars notc2;
178 struct ltchars noltc2;
179 static struct tchars notc = { -1, -1, -1, -1, -1, -1 };
180 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
181
182 globalmode = f;
183 if (prevmode == f)
184 return;
185 old = prevmode;
186 prevmode = f;
187 sb = nttyb;
188
189 switch (f) {
190
191 case 0:
192 onoff = 0;
193 tc = &otc;
194 ltc = &oltc;
195 break;
196
197 case 1: /* remote character processing, remote echo */
198 case 2: /* remote character processing, local echo */
199 case 6: /* 3270 mode - like 1, but with xon/xoff local */
200 /* (might be nice to have "6" in telnet also...) */
201 sb.sg_flags |= CBREAK;
202 if ((f == 1) || (f == 6)) {
203 sb.sg_flags &= ~(ECHO|CRMOD);
204 } else {
205 sb.sg_flags |= ECHO|CRMOD;
206 }
207 sb.sg_erase = sb.sg_kill = -1;
208 if (f == 6) {
209 tc = &tc3;
210 tc3 = notc;
211 /* get XON, XOFF characters */
212 tc3.t_startc = otc.t_startc;
213 tc3.t_stopc = otc.t_stopc;
214 } else {
215 /*
216 * If user hasn't specified one way or the other,
217 * then default to not trapping signals.
218 */
219 if (!donelclchars) {
220 localchars = 0;
221 }
222 if (localchars) {
223 notc2 = notc;
224 notc2.t_intrc = ntc.t_intrc;
225 notc2.t_quitc = ntc.t_quitc;
226 tc = &notc2;
227 } else {
228 tc = &notc;
229 }
230 }
231 ltc = &noltc;
232 onoff = 1;
233 break;
234 case 3: /* local character processing, remote echo */
235 case 4: /* local character processing, local echo */
236 case 5: /* local character processing, no echo */
237 sb.sg_flags &= ~CBREAK;
238 sb.sg_flags |= CRMOD;
239 if (f == 4)
240 sb.sg_flags |= ECHO;
241 else
242 sb.sg_flags &= ~ECHO;
243 notc2 = ntc;
244 tc = &notc2;
245 noltc2 = oltc;
246 ltc = &noltc2;
247 /*
248 * If user hasn't specified one way or the other,
249 * then default to trapping signals.
250 */
251 if (!donelclchars) {
252 localchars = 1;
253 }
254 if (localchars) {
255 notc2.t_brkc = nltc.t_flushc;
256 noltc2.t_flushc = -1;
257 } else {
258 notc2.t_intrc = notc2.t_quitc = -1;
259 }
260 noltc2.t_suspc = escape;
261 noltc2.t_dsuspc = -1;
262 onoff = 1;
263 break;
264
265 default:
266 return;
267 }
268 ioctl(fd_in, TIOCSLTC, (char *)ltc);
269 ioctl(fd_in, TIOCSETC, (char *)tc);
270 ioctl(fd_in, TIOCSETP, (char *)&sb);
271#if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
272 ioctl(fd_in, FIONBIO, (char *)&onoff);
273 ioctl(fd_out, FIONBIO, (char *)&onoff);
274#endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
275#if defined(TN3270)
276 if (noasynch == 0) {
277 ioctl(fd_in, FIOASYNC, (char *)&onoff);
278 }
279#endif /* defined(TN3270) */
280
281 if (MODE_LINE(f)) {
282 void doescape();
283
284 signal(SIGTSTP, doescape);
285 } else if (MODE_LINE(old)) {
286 signal(SIGTSTP, SIG_DFL);
287 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
288 }
289}
290
291
292int
293NetClose(net)
294int net;
295{
296 return close(net);
297}
298
299
300void
310e7d0b 301NetNonblockingIO(fd, onoff)
e767b7ab
GM
302int
303 fd,
304 onoff;
305{
306 ioctl(fd, FIONBIO, (char *)&onoff);
307}
308
309void
310e7d0b 310NetSigIO(fd, onoff)
e767b7ab
GM
311int
312 fd,
313 onoff;
314{
315 ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */
316}
317
318void
310e7d0b 319NetSetPgrp(fd)
e767b7ab
GM
320int fd;
321{
322 int myPid;
323
324 myPid = getpid();
325#if defined(NOT43)
326 myPid = -myPid;
327#endif /* defined(NOT43) */
328 ioctl(fd, SIOCSPGRP, (char *)&myPid); /* set my pid */
329}
310e7d0b
GM
330\f
331/*
332 * Various signal handling routines.
333 */
334
335void
336deadpeer()
337{
338 setcommandmode();
339 longjmp(peerdied, -1);
340}
341
342void
343intr()
344{
345 if (localchars) {
346 intp();
347 return;
348 }
349 setcommandmode();
350 longjmp(toplevel, -1);
351}
e767b7ab 352
310e7d0b
GM
353void
354intr2()
355{
356 if (localchars) {
357 sendbrk();
358 return;
359 }
360}
e767b7ab 361
310e7d0b
GM
362void
363doescape()
364{
365 command(0);
366}
367\f
b307f09e
GM
368void
369sys_telnet_init()
370{
310e7d0b 371#if defined(TN3270)
b307f09e
GM
372 int myPid;
373#endif /* defined(TN3270) */
374
310e7d0b
GM
375 signal(SIGINT, intr);
376 signal(SIGQUIT, intr2);
377 signal(SIGPIPE, deadpeer);
378
b307f09e
GM
379 setconnmode();
380
381 NetNonblockingIO(net, 1);
382
383#if defined(TN3270)
384 if (noasynch == 0) { /* DBX can't handle! */
385 NetSigIO(net, 1);
386 NetSetPgrp(net);
387 }
388#endif /* defined(TN3270) */
389
390#if defined(SO_OOBINLINE)
391 SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1);
392#endif /* defined(SO_OOBINLINE) */
393}
394
395/*
396 * Process rings -
397 *
398 * This routine tries to fill up/empty our various rings.
399 *
400 * The parameter specifies whether this is a poll operation,
401 * or a block-until-something-happens operation.
402 *
403 * The return value is 1 if something happened, 0 if not.
404 */
405
406int
407process_rings(netin, netout, netex, ttyin, ttyout, poll)
408int poll; /* If 0, then block until something to do */
409{
410 register int c;
411 /* One wants to be a bit careful about setting returnValue
412 * to one, since a one implies we did some useful work,
413 * and therefore probably won't be called to block next
414 * time (TN3270 mode only).
415 */
416 int returnValue = 0;
417 static struct timeval TimeValue = { 0 };
418
419 if (netout) {
420 FD_SET(net, &obits);
421 }
422 if (ttyout) {
423 FD_SET(tout, &obits);
424 }
425#if defined(TN3270)
426 if (ttyin) {
427 FD_SET(tin, &ibits);
428 }
429#else /* defined(TN3270) */
430 if (ttyin) {
431 FD_SET(tin, &ibits);
432 }
433#endif /* defined(TN3270) */
434#if defined(TN3270)
435 if (netin) {
436 FD_SET(net, &ibits);
437 }
438# else /* !defined(TN3270) */
439 if (netin) {
440 FD_SET(net, &ibits);
441 }
442# endif /* !defined(TN3270) */
443 if (netex) {
444 FD_SET(net, &xbits);
445 }
446 if ((c = select(16, &ibits, &obits, &xbits,
447 (poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
448 if (c == -1) {
449 /*
450 * we can get EINTR if we are in line mode,
451 * and the user does an escape (TSTP), or
452 * some other signal generator.
453 */
454 if (errno == EINTR) {
455 return 0;
456 }
457# if defined(TN3270)
458 /*
459 * we can get EBADF if we were in transparent
460 * mode, and the transcom process died.
461 */
462 if (errno == EBADF) {
463 /*
464 * zero the bits (even though kernel does it)
465 * to make sure we are selecting on the right
466 * ones.
467 */
468 FD_ZERO(&ibits);
469 FD_ZERO(&obits);
470 FD_ZERO(&xbits);
471 return 0;
472 }
473# endif /* defined(TN3270) */
474 /* I don't like this, does it ever happen? */
475 printf("sleep(5) from telnet, after select\r\n");
b307f09e 476 sleep(5);
b307f09e
GM
477 }
478 return 0;
479 }
480
481 /*
482 * Any urgent data?
483 */
484 if (FD_ISSET(net, &xbits)) {
485 FD_CLR(net, &xbits);
486 SYNCHing = 1;
487 ttyflush(1); /* flush already enqueued data */
488 }
489
490 /*
491 * Something to read from the network...
492 */
493 if (FD_ISSET(net, &ibits)) {
494 int canread;
495
496 FD_CLR(net, &ibits);
497 canread = ring_empty_consecutive(&netiring);
498#if !defined(SO_OOBINLINE)
499 /*
500 * In 4.2 (and some early 4.3) systems, the
501 * OOB indication and data handling in the kernel
502 * is such that if two separate TCP Urgent requests
503 * come in, one byte of TCP data will be overlaid.
504 * This is fatal for Telnet, but we try to live
505 * with it.
506 *
507 * In addition, in 4.2 (and...), a special protocol
508 * is needed to pick up the TCP Urgent data in
509 * the correct sequence.
510 *
511 * What we do is: if we think we are in urgent
512 * mode, we look to see if we are "at the mark".
513 * If we are, we do an OOB receive. If we run
514 * this twice, we will do the OOB receive twice,
515 * but the second will fail, since the second
516 * time we were "at the mark", but there wasn't
517 * any data there (the kernel doesn't reset
518 * "at the mark" until we do a normal read).
519 * Once we've read the OOB data, we go ahead
520 * and do normal reads.
521 *
522 * There is also another problem, which is that
523 * since the OOB byte we read doesn't put us
524 * out of OOB state, and since that byte is most
525 * likely the TELNET DM (data mark), we would
526 * stay in the TELNET SYNCH (SYNCHing) state.
527 * So, clocks to the rescue. If we've "just"
528 * received a DM, then we test for the
529 * presence of OOB data when the receive OOB
530 * fails (and AFTER we did the normal mode read
531 * to clear "at the mark").
532 */
533 if (SYNCHing) {
534 int atmark;
535
536 ioctl(net, SIOCATMARK, (char *)&atmark);
537 if (atmark) {
538 c = recv(net, netiring.supply, canread, MSG_OOB);
539 if ((c == -1) && (errno == EINVAL)) {
540 c = recv(net, netiring.supply, canread, 0);
541 if (clocks.didnetreceive < clocks.gotDM) {
542 SYNCHing = stilloob(net);
543 }
544 }
545 } else {
546 c = recv(net, netiring.supply, canread, 0);
547 }
548 } else {
549 c = recv(net, netiring.supply, canread, 0);
550 }
551 settimer(didnetreceive);
552#else /* !defined(SO_OOBINLINE) */
553 c = recv(net, netiring.supply, canread, 0);
554#endif /* !defined(SO_OOBINLINE) */
555 if (c < 0 && errno == EWOULDBLOCK) {
556 c = 0;
557 } else if (c <= 0) {
558 return -1;
559 }
560 if (netdata) {
561 Dump('<', netiring.supply, c);
562 }
ad1c581e
GM
563 if (c)
564 ring_supplied(&netiring, c);
b307f09e
GM
565 returnValue = 1;
566 }
567
568 /*
569 * Something to read from the tty...
570 */
571 if (FD_ISSET(tin, &ibits)) {
572 FD_CLR(tin, &ibits);
573 c = TerminalRead(tin, ttyiring.supply,
574 ring_empty_consecutive(&ttyiring));
575 if (c < 0 && errno == EWOULDBLOCK) {
576 c = 0;
577 } else {
b307f09e
GM
578 /* EOF detection for line mode!!!! */
579 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
580 /* must be an EOF... */
581 *ttyiring.supply = termEofChar;
582 c = 1;
583 }
b307f09e
GM
584 if (c <= 0) {
585 return -1;
586 }
ad1c581e 587 ring_supplied(&ttyiring, c);
b307f09e 588 }
b307f09e
GM
589 returnValue = 1; /* did something useful */
590 }
591
592 if (FD_ISSET(net, &obits)) {
593 FD_CLR(net, &obits);
594 returnValue |= netflush();
595 }
596 if (FD_ISSET(tout, &obits)) {
597 FD_CLR(tout, &obits);
598 returnValue |= ttyflush(SYNCHing|flushout);
599 }
600
601 return returnValue;
602}
e767b7ab 603#endif /* defined(unix) */