xref: /original-bsd/libexec/telnetd/termstat.c (revision 76210d32)
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)termstat.c	5.5 (Berkeley) 06/01/90";
10 #endif /* not lint */
11 
12 #include "telnetd.h"
13 
14 /*
15  * local variables
16  */
17 #ifdef	LINEMODE
18 static int _terminit = 0;
19 static int def_tspeed = -1, def_rspeed = -1;
20 #ifdef	TIOCSWINSZ
21 static int def_row = 0, def_col = 0;
22 #endif
23 #endif	LINEMODE
24 
25 #if	defined(CRAY2) && defined(UNICOS5)
26 int	newmap = 1;	/* nonzero if \n maps to ^M^J */
27 #endif
28 
29 #ifdef	LINEMODE
30 /*
31  * localstat
32  *
33  * This function handles all management of linemode.
34  *
35  * Linemode allows the client to do the local editing of data
36  * and send only complete lines to the server.  Linemode state is
37  * based on the state of the pty driver.  If the pty is set for
38  * external processing, then we can use linemode.  Further, if we
39  * can use real linemode, then we can look at the edit control bits
40  * in the pty to determine what editing the client should do.
41  *
42  * Linemode support uses the following state flags to keep track of
43  * current and desired linemode state.
44  *	alwayslinemode : true if -l was specified on the telnetd
45  * 	command line.  It means to have linemode on as much as
46  *	possible.
47  *
48  * 	lmodetype: signifies whether the client can
49  *	handle real linemode, or if use of kludgeomatic linemode
50  *	is preferred.  It will be set to one of the following:
51  *		REAL_LINEMODE : use linemode option
52  *		KLUDGE_LINEMODE : use kludge linemode
53  *		NO_LINEMODE : client is ignorant of linemode
54  *
55  *	linemode, uselinemode : linemode is true if linemode
56  *	is currently on, uselinemode is the state that we wish
57  *	to be in.  If another function wishes to turn linemode
58  *	on or off, it sets or clears uselinemode.
59  *
60  *	editmode, useeditmode : like linemode/uselinemode, but
61  *	these contain the edit mode states (edit and trapsig).
62  *
63  * The state variables correspond to some of the state information
64  * in the pty.
65  *	linemode:
66  *		In real linemode, this corresponds to whether the pty
67  *		expects external processing of incoming data.
68  *		In kludge linemode, this more closely corresponds to the
69  *		whether normal processing is on or not.  (ICANON in
70  *		system V, or COOKED mode in BSD.)
71  *		If the -l option was specified (alwayslinemode), then
72  *		an attempt is made to force external processing on at
73  *		all times.
74  *
75  * The following heuristics are applied to determine linemode
76  * handling within the server.
77  *	1) Early on in starting up the server, an attempt is made
78  *	   to negotiate the linemode option.  If this succeeds
79  *	   then lmodetype is set to REAL_LINEMODE and all linemode
80  *	   processing occurs in the context of the linemode option.
81  *	2) If the attempt to negotiate the linemode option failed,
82  *	   then we try to use kludge linemode.  We test for this
83  *	   capability by sending "do Timing Mark".  If a positive
84  *	   response comes back, then we assume that the client
85  *	   understands kludge linemode (ech!) and the
86  *	   lmodetype flag is set to KLUDGE_LINEMODE.
87  *	3) Otherwise, linemode is not supported at all and
88  *	   lmodetype remains set to NO_LINEMODE (which happens
89  *	   to be 0 for convenience).
90  *	4) At any time a command arrives that implies a higher
91  *	   state of linemode support in the client, we move to that
92  *	   linemode support.
93  *
94  * A short explanation of kludge linemode is in order here.
95  *	1) The heuristic to determine support for kludge linemode
96  *	   is to send a do timing mark.  We assume that a client
97  *	   that supports timing marks also supports kludge linemode.
98  *	   A risky proposition at best.
99  *	2) Further negotiation of linemode is done by changing the
100  *	   the server's state regarding SGA.  If server will SGA,
101  *	   then linemode is off, if server won't SGA, then linemode
102  *	   is on.
103  */
104 localstat()
105 {
106 	void netflush();
107 
108 #ifdef	defined(CRAY2) && defined(UNICOS5)
109 	/*
110 	 * Keep track of that ol' CR/NL mapping while we're in the
111 	 * neighborhood.
112 	 */
113 	newmap = tty_isnewmap();
114 #endif	defined(CRAY2) && defined(UNICOS5)
115 
116 	/*
117 	 * Check for state of BINARY options.
118 	 */
119 	if (tty_isbinaryin()) {
120 		if (hiswants[TELOPT_BINARY] == OPT_NO)
121 			send_do(TELOPT_BINARY, 1);
122 	} else {
123 		if (hiswants[TELOPT_BINARY] == OPT_YES)
124 			send_dont(TELOPT_BINARY, 1);
125 	}
126 
127 	if (tty_isbinaryout()) {
128 		if (mywants[TELOPT_BINARY] == OPT_NO)
129 			send_will(TELOPT_BINARY, 1);
130 	} else {
131 		if (mywants[TELOPT_BINARY] == OPT_YES)
132 			send_wont(TELOPT_BINARY, 1);
133 	}
134 
135 	/*
136 	 * Check for changes to flow control if client supports it.
137 	 */
138 	if (hisopts[TELOPT_LFLOW] == OPT_YES) {
139 		if (tty_flowmode() != flowmode) {
140 			flowmode = tty_flowmode();
141 			(void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB,
142 				TELOPT_LFLOW, flowmode, IAC, SE);
143 			nfrontp += 6;
144 		}
145 	}
146 
147 	/*
148 	 * Check linemode on/off state
149 	 */
150 	uselinemode = tty_linemode();
151 
152 	/*
153 	 * If alwayslinemode is on, and pty is changing to turn it off, then
154 	 * force linemode back on.
155 	 */
156 	if (alwayslinemode && linemode && !uselinemode) {
157 		uselinemode = 1;
158 		tty_setlinemode(uselinemode);
159 	}
160 
161 # ifdef	KLUDGELINEMODE
162 	/*
163 	 * If using kludge linemode and linemode is desired, it can't
164 	 * be done if normal line editing is not available on the
165 	 * pty.  This becomes the test for linemode on/off when
166 	 * using kludge linemode.
167 	 */
168 	if (lmodetype == KLUDGE_LINEMODE && uselinemode && tty_israw()) {
169 		uselinemode = 0;
170 		tty_setlinemode(uselinemode);
171 	}
172 # endif	/* KLUDGELINEMODE */
173 
174 	/*
175 	 * Do echo mode handling as soon as we know what the
176 	 * linemode is going to be.
177 	 * If the pty has echo turned off, then tell the client that
178 	 * the server will echo.  If echo is on, then the server
179 	 * will echo if in character mode, but in linemode the
180 	 * client should do local echoing.  The state machine will
181 	 * not send anything if it is unnecessary, so don't worry
182 	 * about that here.
183 	 */
184 	if (tty_isecho() && uselinemode)
185 		send_wont(TELOPT_ECHO, 1);
186 	else
187 		send_will(TELOPT_ECHO, 1);
188 
189 	/*
190 	 * If linemode is being turned off, send appropriate
191 	 * command and then we're all done.
192 	 */
193 	 if (!uselinemode && linemode) {
194 # ifdef	KLUDGELINEMODE
195 		if (lmodetype == REAL_LINEMODE)
196 # endif	/* KLUDGELINEMODE */
197 			send_dont(TELOPT_LINEMODE, 1);
198 # ifdef	KLUDGELINEMODE
199 		else if (lmodetype == KLUDGE_LINEMODE)
200 			send_will(TELOPT_SGA, 1);
201 # endif	/* KLUDGELINEMODE */
202 		linemode = uselinemode;
203 		goto done;
204 	}
205 
206 # ifdef	KLUDGELINEMODE
207 	/*
208 	 * If using real linemode check edit modes for possible later use.
209 	 */
210 	if (lmodetype == REAL_LINEMODE) {
211 # endif	/* KLUDGELINEMODE */
212 		useeditmode = 0;
213 		if (tty_isediting())
214 			useeditmode |= MODE_EDIT;
215 		if (tty_istrapsig())
216 			useeditmode |= MODE_TRAPSIG;
217 # ifdef	KLUDGELINEMODE
218 	}
219 # endif	/* KLUDGELINEMODE */
220 
221 	/*
222 	 * Negotiate linemode on if pty state has changed to turn it on.
223 	 * Send appropriate command and send along edit mode, then all done.
224 	 */
225 	if (uselinemode && !linemode) {
226 # ifdef	KLUDGELINEMODE
227 		if (lmodetype == KLUDGE_LINEMODE) {
228 			send_wont(TELOPT_SGA, 1);
229 		} else if (lmodetype == REAL_LINEMODE) {
230 # endif	/* KLUDGELINEMODE */
231 			send_do(TELOPT_LINEMODE, 1);
232 			/* send along edit modes */
233 			(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
234 				TELOPT_LINEMODE, LM_MODE, useeditmode,
235 				IAC, SE);
236 			nfrontp += 7;
237 			editmode = useeditmode;
238 # ifdef	KLUDGELINEMODE
239 		}
240 # endif	/* KLUDGELINEMODE */
241 		linemode = uselinemode;
242 		goto done;
243 	}
244 
245 # ifdef	KLUDGELINEMODE
246 	/*
247 	 * None of what follows is of any value if not using
248 	 * real linemode.
249 	 */
250 	if (lmodetype < REAL_LINEMODE)
251 		goto done;
252 # endif	/* KLUDGELINEMODE */
253 
254 	if (linemode) {
255 		/*
256 		 * If edit mode changed, send edit mode.
257 		 */
258 		 if (useeditmode != editmode) {
259 			/*
260 			 * Send along appropriate edit mode mask.
261 			 */
262 			(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
263 				TELOPT_LINEMODE, LM_MODE, useeditmode,
264 				IAC, SE);
265 			nfrontp += 7;
266 			editmode = useeditmode;
267 		}
268 
269 
270 		/*
271 		 * Check for changes to special characters in use.
272 		 */
273 		start_slc(0);
274 		check_slc();
275 		end_slc(0);
276 	}
277 
278 done:
279 	/*
280 	 * Some things should be deferred until after the pty state has
281 	 * been set by the local process.  Do those things that have been
282 	 * deferred now.  This only happens once.
283 	 */
284 	if (_terminit == 0) {
285 		_terminit = 1;
286 		defer_terminit();
287 	}
288 
289 	netflush();
290 	set_termbuf();
291 	return;
292 
293 }  /* end of localstat */
294 #endif	/* LINEMODE */
295 
296 
297 /*
298  * clientstat
299  *
300  * Process linemode related requests from the client.
301  * Client can request a change to only one of linemode, editmode or slc's
302  * at a time, and if using kludge linemode, then only linemode may be
303  * affected.
304  */
305 clientstat(code, parm1, parm2)
306 register int code, parm1, parm2;
307 {
308 	void netflush();
309 
310 	/*
311 	 * Get a copy of terminal characteristics.
312 	 */
313 	init_termbuf();
314 
315 	/*
316 	 * Process request from client. code tells what it is.
317 	 */
318 	switch (code) {
319 #ifdef	LINEMODE
320 	case TELOPT_LINEMODE:
321 		/*
322 		 * Don't do anything unless client is asking us to change
323 		 * modes.
324 		 */
325 		uselinemode = (parm1 == WILL);
326 		if (uselinemode != linemode) {
327 # ifdef	KLUDGELINEMODE
328 			/*
329 			 * If using kludge linemode, make sure that
330 			 * we can do what the client asks.
331 			 * We can not turn off linemode if alwayslinemode
332 			 * and the ICANON bit is set.
333 			 */
334 			if (lmodetype == KLUDGE_LINEMODE) {
335 				if (alwayslinemode && tty_isediting()) {
336 					uselinemode = 1;
337 				}
338 			}
339 
340 			/*
341 			 * Quit now if we can't do it.
342 			 */
343 			if (uselinemode == linemode)
344 				return;
345 
346 			/*
347 			 * If using real linemode and linemode is being
348 			 * turned on, send along the edit mode mask.
349 			 */
350 			if (lmodetype == REAL_LINEMODE && uselinemode)
351 # else	/* KLUDGELINEMODE */
352 			if (uselinemode)
353 # endif	/* KLUDGELINEMODE */
354 			{
355 				useeditmode = 0;
356 				if (tty_isediting())
357 					useeditmode |= MODE_EDIT;
358 				if (tty_istrapsig)
359 					useeditmode |= MODE_TRAPSIG;
360 				(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
361 					SB, TELOPT_LINEMODE, LM_MODE,
362 							useeditmode, IAC, SE);
363 				nfrontp += 7;
364 				editmode = useeditmode;
365 			}
366 
367 
368 			tty_setlinemode(uselinemode);
369 
370 			linemode = uselinemode;
371 
372 		}
373 		break;
374 
375 	case LM_MODE:
376 	    {
377 		register int mode, sig, ack;
378 
379 		/*
380 		 * Client has sent along a mode mask.  If it agrees with
381 		 * what we are currently doing, ignore it; if not, it could
382 		 * be viewed as a request to change.  Note that the server
383 		 * will change to the modes in an ack if it is different from
384 		 * what we currently have, but we will not ack the ack.
385 		 */
386 		 useeditmode &= MODE_MASK;
387 		 ack = (useeditmode & MODE_ACK);
388 		 useeditmode &= ~MODE_ACK;
389 
390 		 if (useeditmode != editmode) {
391 			mode = (useeditmode & MODE_EDIT);
392 			sig = (useeditmode & MODE_TRAPSIG);
393 
394 			if (mode != (editmode & LM_MODE)) {
395 				tty_setedit(mode);
396 			}
397 			if (sig != (editmode & MODE_TRAPSIG)) {
398 				tty_setsig(sig);
399 			}
400 
401 			set_termbuf();
402 
403  			if (!ack) {
404  				(void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
405 					SB, TELOPT_LINEMODE, LM_MODE,
406  					useeditmode|MODE_ACK,
407  					IAC, SE);
408  				nfrontp += 7;
409  			}
410 
411 			editmode = useeditmode;
412 		}
413 
414 		break;
415 
416 	    }  /* end of case LM_MODE */
417 #endif	/* LINEMODE */
418 
419 	case TELOPT_NAWS:
420 #ifdef	TIOCSWINSZ
421 	    {
422 		struct winsize ws;
423 
424 #ifdef	LINEMODE
425 		/*
426 		 * Defer changing window size until after terminal is
427 		 * initialized.
428 		 */
429 		if (terminit() == 0) {
430 			def_col = parm1;
431 			def_row = parm2;
432 			return;
433 		}
434 #endif	/* LINEMODE */
435 
436 		/*
437 		 * Change window size as requested by client.
438 		 */
439 
440 		ws.ws_col = parm1;
441 		ws.ws_row = parm2;
442 		(void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
443 	    }
444 #endif	/* TIOCSWINSZ */
445 
446 		break;
447 
448 	case TELOPT_TSPEED:
449 	    {
450 #ifdef	LINEMODE
451 		/*
452 		 * Defer changing the terminal speed.
453 		 */
454 		if (terminit() == 0) {
455 			def_tspeed = parm1;
456 			def_rspeed = parm2;
457 			return;
458 		}
459 #endif	/* LINEMODE */
460 		/*
461 		 * Change terminal speed as requested by client.
462 		 */
463 		tty_tspeed(parm1);
464 		tty_rspeed(parm2);
465 		set_termbuf();
466 
467 		break;
468 
469 	    }  /* end of case TELOPT_TSPEED */
470 
471 	default:
472 		/* What? */
473 		break;
474 	}  /* end of switch */
475 
476 #if	defined(CRAY2) && defined(UNICOS5)
477 	/*
478 	 * Just in case of the likely event that we changed the pty state.
479 	 */
480 	rcv_ioctl();
481 #endif	/* defined(CRAY2) && defined(UNICOS5) */
482 
483 	netflush();
484 
485 }  /* end of clientstat */
486 
487 #if	defined(CRAY2) && defined(UNICOS5)
488 termstat()
489 {
490 	needtermstat = 1;
491 }
492 
493 _termstat()
494 {
495 	needtermstat = 0;
496 	init_termbuf();
497 	localstat();
498 	rcv_ioctl();
499 }
500 #endif	/* defined(CRAY2) && defined(UNICOS5) */
501 
502 #ifdef	LINEMODE
503 /*
504  * defer_terminit
505  *
506  * Some things should not be done until after the login process has started
507  * and all the pty modes are set to what they are supposed to be.  This
508  * function is called when the pty state has been processed for the first time.
509  * It calls other functions that do things that were deferred in each module.
510  */
511 defer_terminit()
512 {
513 
514 	/*
515 	 * local stuff that got deferred.
516 	 */
517 	if (def_tspeed != -1) {
518 		clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
519 		def_tspeed = def_rspeed = 0;
520 	}
521 
522 #ifdef	TIOCSWINSZ
523 	if (def_col || def_row) {
524 		struct winsize ws;
525 
526 		ws.ws_col = def_col;
527 		ws.ws_row = def_row;
528 		(void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
529 	}
530 #endif
531 
532 	/*
533 	 * The only other module that currently defers anything.
534 	 */
535 	deferslc();
536 
537 }  /* end of defer_terminit */
538 
539 /*
540  * terminit
541  *
542  * Returns true if the pty state has been processed yet.
543  */
544 int terminit()
545 {
546 	return _terminit;
547 
548 }  /* end of terminit */
549 #endif	/* LINEMODE */
550