rationalized handling of child processes, cleaned up mail1 some more
[unix-history] / usr / src / usr.bin / telnet / sys_bsd.c
CommitLineData
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 19static 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
44int
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
50static struct tchars otc = { 0 }, ntc = { 0 };
51static struct ltchars oltc = { 0 }, nltc = { 0 };
52static struct sgttyb ottyb = { 0 }, nttyb = { 0 };
53
b307f09e
GM
54static fd_set ibits, obits, xbits;
55
56
57init_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 69TerminalWrite(buf, n)
e767b7ab
GM
70char *buf;
71int n;
72{
448f9c06 73 return write(tout, buf, n);
e767b7ab
GM
74}
75
448f9c06 76TerminalRead(buf, n)
e767b7ab
GM
77char *buf;
78int n;
79{
448f9c06 80 return read(tin, buf, n);
e767b7ab
GM
81}
82
83/*
84 *
85 */
86
87int
310e7d0b 88TerminalAutoFlush()
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
112int
310e7d0b 113TerminalSpecialChars(c)
e767b7ab
GM
114int 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
144void
310e7d0b 145TerminalFlushOutput()
e767b7ab
GM
146{
147 (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
148}
149
150void
310e7d0b 151TerminalSaveState()
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
169void
310e7d0b 170TerminalRestoreState()
e767b7ab
GM
171{
172}
173
174/*
175 * TerminalNewMode - set up terminal to a specific mode.
176 */
177
178
179void
448f9c06 180TerminalNewMode(f)
e767b7ab
GM
181register 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 = &notc2;
240 } else {
241 tc = &notc;
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 = &notc2;
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
305int
306NetClose(net)
307int net;
308{
309 return close(net);
310}
311
312
313void
310e7d0b 314NetNonblockingIO(fd, onoff)
e767b7ab
GM
315int
316 fd,
317 onoff;
318{
319 ioctl(fd, FIONBIO, (char *)&onoff);
320}
321
80a47e22 322#if defined(TN3270)
e767b7ab 323void
310e7d0b 324NetSigIO(fd, onoff)
e767b7ab
GM
325int
326 fd,
327 onoff;
328{
329 ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */
330}
331
332void
310e7d0b 333NetSetPgrp(fd)
e767b7ab
GM
334int 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 350static void
310e7d0b
GM
351deadpeer()
352{
353 setcommandmode();
354 longjmp(peerdied, -1);
355}
356
448f9c06 357static void
310e7d0b
GM
358intr()
359{
360 if (localchars) {
361 intp();
362 return;
363 }
364 setcommandmode();
365 longjmp(toplevel, -1);
366}
e767b7ab 367
448f9c06 368static void
310e7d0b
GM
369intr2()
370{
371 if (localchars) {
372 sendbrk();
373 return;
374 }
375}
e767b7ab 376
448f9c06 377static void
310e7d0b
GM
378doescape()
379{
380 command(0);
381}
382\f
b307f09e
GM
383void
384sys_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
423int
424process_rings(netin, netout, netex, ttyin, ttyout, poll)
425int 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) */