Commit | Line | Data |
---|---|---|
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 | ||
23 | int | |
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) | |
30 | static char tline[200]; | |
31 | char *transcom = 0; /* transparent mode command (default: none) */ | |
32 | #endif /* defined(TN3270) */ | |
33 | ||
34 | static struct tchars otc = { 0 }, ntc = { 0 }; | |
35 | static struct ltchars oltc = { 0 }, nltc = { 0 }; | |
36 | static struct sgttyb ottyb = { 0 }, nttyb = { 0 }; | |
37 | ||
b307f09e GM |
38 | static fd_set ibits, obits, xbits; |
39 | ||
40 | ||
41 | init_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 | |
53 | TerminalWrite(fd, buf, n) | |
54 | int fd; | |
55 | char *buf; | |
56 | int n; | |
57 | { | |
58 | return write(fd, buf, n); | |
59 | } | |
60 | ||
61 | TerminalRead(fd, buf, n) | |
62 | int fd; | |
63 | char *buf; | |
64 | int n; | |
65 | { | |
66 | return read(fd, buf, n); | |
67 | } | |
68 | ||
69 | /* | |
70 | * | |
71 | */ | |
72 | ||
73 | int | |
310e7d0b | 74 | TerminalAutoFlush() |
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 | ||
98 | int | |
310e7d0b | 99 | TerminalSpecialChars(c) |
e767b7ab GM |
100 | int 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 | ||
130 | void | |
310e7d0b | 131 | TerminalFlushOutput() |
e767b7ab GM |
132 | { |
133 | (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0); | |
134 | } | |
135 | ||
136 | void | |
310e7d0b | 137 | TerminalSaveState() |
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 | ||
155 | void | |
310e7d0b | 156 | TerminalRestoreState() |
e767b7ab GM |
157 | { |
158 | } | |
159 | ||
160 | /* | |
161 | * TerminalNewMode - set up terminal to a specific mode. | |
162 | */ | |
163 | ||
164 | ||
165 | void | |
310e7d0b | 166 | TerminalNewMode(fd_in, fd_out, f) |
e767b7ab GM |
167 | int fd_in, fd_out; /* File descriptor */ |
168 | register 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 = ¬c2; | |
227 | } else { | |
228 | tc = ¬c; | |
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 = ¬c2; | |
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 | ||
292 | int | |
293 | NetClose(net) | |
294 | int net; | |
295 | { | |
296 | return close(net); | |
297 | } | |
298 | ||
299 | ||
300 | void | |
310e7d0b | 301 | NetNonblockingIO(fd, onoff) |
e767b7ab GM |
302 | int |
303 | fd, | |
304 | onoff; | |
305 | { | |
306 | ioctl(fd, FIONBIO, (char *)&onoff); | |
307 | } | |
308 | ||
309 | void | |
310e7d0b | 310 | NetSigIO(fd, onoff) |
e767b7ab GM |
311 | int |
312 | fd, | |
313 | onoff; | |
314 | { | |
315 | ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */ | |
316 | } | |
317 | ||
318 | void | |
310e7d0b | 319 | NetSetPgrp(fd) |
e767b7ab GM |
320 | int 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 | ||
335 | void | |
336 | deadpeer() | |
337 | { | |
338 | setcommandmode(); | |
339 | longjmp(peerdied, -1); | |
340 | } | |
341 | ||
342 | void | |
343 | intr() | |
344 | { | |
345 | if (localchars) { | |
346 | intp(); | |
347 | return; | |
348 | } | |
349 | setcommandmode(); | |
350 | longjmp(toplevel, -1); | |
351 | } | |
e767b7ab | 352 | |
310e7d0b GM |
353 | void |
354 | intr2() | |
355 | { | |
356 | if (localchars) { | |
357 | sendbrk(); | |
358 | return; | |
359 | } | |
360 | } | |
e767b7ab | 361 | |
310e7d0b GM |
362 | void |
363 | doescape() | |
364 | { | |
365 | command(0); | |
366 | } | |
367 | \f | |
b307f09e GM |
368 | void |
369 | sys_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 | ||
406 | int | |
407 | process_rings(netin, netout, netex, ttyin, ttyout, poll) | |
408 | int 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) */ |