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