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