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.8 (Berkeley) 02/02/91"; 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 #if 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 (his_want_state_is_wont(TELOPT_BINARY)) 121 send_do(TELOPT_BINARY, 1); 122 } else { 123 if (his_want_state_is_will(TELOPT_BINARY)) 124 send_dont(TELOPT_BINARY, 1); 125 } 126 127 if (tty_isbinaryout()) { 128 if (my_want_state_is_wont(TELOPT_BINARY)) 129 send_will(TELOPT_BINARY, 1); 130 } else { 131 if (my_want_state_is_will(TELOPT_BINARY)) 132 send_wont(TELOPT_BINARY, 1); 133 } 134 135 /* 136 * Check for changes to flow control if client supports it. 137 */ 138 if (his_state_is_will(TELOPT_LFLOW)) { 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 /* 162 * Do echo mode handling as soon as we know what the 163 * linemode is going to be. 164 * If the pty has echo turned off, then tell the client that 165 * the server will echo. If echo is on, then the server 166 * will echo if in character mode, but in linemode the 167 * client should do local echoing. The state machine will 168 * not send anything if it is unnecessary, so don't worry 169 * about that here. 170 */ 171 if (uselinemode) { 172 if (tty_isecho()) 173 send_wont(TELOPT_ECHO, 1); 174 else 175 send_will(TELOPT_ECHO, 1); 176 } 177 178 /* 179 * If linemode is being turned off, send appropriate 180 * command and then we're all done. 181 */ 182 if (!uselinemode && linemode) { 183 # ifdef KLUDGELINEMODE 184 if (lmodetype == REAL_LINEMODE) 185 # endif /* KLUDGELINEMODE */ 186 send_dont(TELOPT_LINEMODE, 1); 187 # ifdef KLUDGELINEMODE 188 else if (lmodetype == KLUDGE_LINEMODE) 189 send_will(TELOPT_SGA, 1); 190 # endif /* KLUDGELINEMODE */ 191 linemode = uselinemode; 192 goto done; 193 } 194 195 # ifdef KLUDGELINEMODE 196 /* 197 * If using real linemode check edit modes for possible later use. 198 * If we are in kludge linemode, do the SGA negotiation. 199 */ 200 if (lmodetype == REAL_LINEMODE) { 201 # endif /* KLUDGELINEMODE */ 202 useeditmode = 0; 203 if (tty_isediting()) 204 useeditmode |= MODE_EDIT; 205 if (tty_istrapsig()) 206 useeditmode |= MODE_TRAPSIG; 207 if (tty_issofttab()) 208 useeditmode |= MODE_SOFT_TAB; 209 if (tty_islitecho()) 210 useeditmode |= MODE_LIT_ECHO; 211 # ifdef KLUDGELINEMODE 212 } else if (lmodetype == KLUDGE_LINEMODE) { 213 if (tty_isediting() && uselinemode) 214 send_wont(TELOPT_SGA, 1); 215 else 216 send_will(TELOPT_SGA, 1); 217 } 218 # endif /* KLUDGELINEMODE */ 219 220 /* 221 * Negotiate linemode on if pty state has changed to turn it on. 222 * Send appropriate command and send along edit mode, then all done. 223 */ 224 if (uselinemode && !linemode) { 225 # ifdef KLUDGELINEMODE 226 if (lmodetype == KLUDGE_LINEMODE) { 227 send_wont(TELOPT_SGA, 1); 228 } else if (lmodetype == REAL_LINEMODE) { 229 # endif /* KLUDGELINEMODE */ 230 send_do(TELOPT_LINEMODE, 1); 231 /* send along edit modes */ 232 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, 233 TELOPT_LINEMODE, LM_MODE, useeditmode, 234 IAC, SE); 235 nfrontp += 7; 236 editmode = useeditmode; 237 # ifdef KLUDGELINEMODE 238 } 239 # endif /* KLUDGELINEMODE */ 240 linemode = uselinemode; 241 goto done; 242 } 243 244 # ifdef KLUDGELINEMODE 245 /* 246 * None of what follows is of any value if not using 247 * real linemode. 248 */ 249 if (lmodetype < REAL_LINEMODE) 250 goto done; 251 # endif /* KLUDGELINEMODE */ 252 253 if (linemode && his_state_is_will(TELOPT_LINEMODE)) { 254 /* 255 * If edit mode changed, send edit mode. 256 */ 257 if (useeditmode != editmode) { 258 /* 259 * Send along appropriate edit mode mask. 260 */ 261 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, 262 TELOPT_LINEMODE, LM_MODE, useeditmode, 263 IAC, SE); 264 nfrontp += 7; 265 editmode = useeditmode; 266 } 267 268 269 /* 270 * Check for changes to special characters in use. 271 */ 272 start_slc(0); 273 check_slc(); 274 end_slc(0); 275 } 276 277 done: 278 /* 279 * Some things should be deferred until after the pty state has 280 * been set by the local process. Do those things that have been 281 * deferred now. This only happens once. 282 */ 283 if (_terminit == 0) { 284 _terminit = 1; 285 defer_terminit(); 286 } 287 288 netflush(); 289 set_termbuf(); 290 return; 291 292 } /* end of localstat */ 293 #endif /* LINEMODE */ 294 295 296 /* 297 * clientstat 298 * 299 * Process linemode related requests from the client. 300 * Client can request a change to only one of linemode, editmode or slc's 301 * at a time, and if using kludge linemode, then only linemode may be 302 * affected. 303 */ 304 clientstat(code, parm1, parm2) 305 register int code, parm1, parm2; 306 { 307 void netflush(); 308 309 /* 310 * Get a copy of terminal characteristics. 311 */ 312 init_termbuf(); 313 314 /* 315 * Process request from client. code tells what it is. 316 */ 317 switch (code) { 318 #ifdef LINEMODE 319 case TELOPT_LINEMODE: 320 /* 321 * Don't do anything unless client is asking us to change 322 * modes. 323 */ 324 uselinemode = (parm1 == WILL); 325 if (uselinemode != linemode) { 326 # ifdef KLUDGELINEMODE 327 /* 328 * If using kludge linemode, make sure that 329 * we can do what the client asks. 330 * We can not turn off linemode if alwayslinemode 331 * and the ICANON bit is set. 332 */ 333 if (lmodetype == KLUDGE_LINEMODE) { 334 if (alwayslinemode && tty_isediting()) { 335 uselinemode = 1; 336 } 337 } 338 339 /* 340 * Quit now if we can't do it. 341 */ 342 if (uselinemode == linemode) 343 return; 344 345 /* 346 * If using real linemode and linemode is being 347 * turned on, send along the edit mode mask. 348 */ 349 if (lmodetype == REAL_LINEMODE && uselinemode) 350 # else /* KLUDGELINEMODE */ 351 if (uselinemode) 352 # endif /* KLUDGELINEMODE */ 353 { 354 useeditmode = 0; 355 if (tty_isediting()) 356 useeditmode |= MODE_EDIT; 357 if (tty_istrapsig()) 358 useeditmode |= MODE_TRAPSIG; 359 if (tty_issofttab()) 360 useeditmode |= MODE_SOFT_TAB; 361 if (tty_islitecho()) 362 useeditmode |= MODE_LIT_ECHO; 363 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, 364 SB, TELOPT_LINEMODE, LM_MODE, 365 useeditmode, IAC, SE); 366 nfrontp += 7; 367 editmode = useeditmode; 368 } 369 370 371 tty_setlinemode(uselinemode); 372 373 linemode = uselinemode; 374 375 } 376 break; 377 378 case LM_MODE: 379 { 380 register int ack, changed; 381 382 /* 383 * Client has sent along a mode mask. If it agrees with 384 * what we are currently doing, ignore it; if not, it could 385 * be viewed as a request to change. Note that the server 386 * will change to the modes in an ack if it is different from 387 * what we currently have, but we will not ack the ack. 388 */ 389 useeditmode &= MODE_MASK; 390 ack = (useeditmode & MODE_ACK); 391 useeditmode &= ~MODE_ACK; 392 393 if (changed = (useeditmode ^ editmode)) { 394 if (changed & MODE_EDIT) 395 tty_setedit(useeditmode & MODE_EDIT); 396 397 if (changed & MODE_TRAPSIG) 398 tty_setsig(useeditmode & MODE_TRAPSIG); 399 400 if (changed & MODE_SOFT_TAB) 401 tty_setsofttab(useeditmode & MODE_SOFT_TAB); 402 403 if (changed & MODE_LIT_ECHO) 404 tty_setlitecho(useeditmode & MODE_LIT_ECHO); 405 406 set_termbuf(); 407 408 if (!ack) { 409 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, 410 SB, TELOPT_LINEMODE, LM_MODE, 411 useeditmode|MODE_ACK, 412 IAC, SE); 413 nfrontp += 7; 414 } 415 416 editmode = useeditmode; 417 } 418 419 break; 420 421 } /* end of case LM_MODE */ 422 #endif /* LINEMODE */ 423 424 case TELOPT_NAWS: 425 #ifdef TIOCSWINSZ 426 { 427 struct winsize ws; 428 429 #ifdef LINEMODE 430 /* 431 * Defer changing window size until after terminal is 432 * initialized. 433 */ 434 if (terminit() == 0) { 435 def_col = parm1; 436 def_row = parm2; 437 return; 438 } 439 #endif /* LINEMODE */ 440 441 /* 442 * Change window size as requested by client. 443 */ 444 445 ws.ws_col = parm1; 446 ws.ws_row = parm2; 447 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); 448 } 449 #endif /* TIOCSWINSZ */ 450 451 break; 452 453 case TELOPT_TSPEED: 454 { 455 #ifdef LINEMODE 456 /* 457 * Defer changing the terminal speed. 458 */ 459 if (terminit() == 0) { 460 def_tspeed = parm1; 461 def_rspeed = parm2; 462 return; 463 } 464 #endif /* LINEMODE */ 465 /* 466 * Change terminal speed as requested by client. 467 */ 468 tty_tspeed(parm1); 469 tty_rspeed(parm2); 470 set_termbuf(); 471 472 break; 473 474 } /* end of case TELOPT_TSPEED */ 475 476 default: 477 /* What? */ 478 break; 479 } /* end of switch */ 480 481 #if defined(CRAY2) && defined(UNICOS5) 482 /* 483 * Just in case of the likely event that we changed the pty state. 484 */ 485 rcv_ioctl(); 486 #endif /* defined(CRAY2) && defined(UNICOS5) */ 487 488 netflush(); 489 490 } /* end of clientstat */ 491 492 #if defined(CRAY2) && defined(UNICOS5) 493 termstat() 494 { 495 needtermstat = 1; 496 } 497 498 _termstat() 499 { 500 needtermstat = 0; 501 init_termbuf(); 502 localstat(); 503 rcv_ioctl(); 504 } 505 #endif /* defined(CRAY2) && defined(UNICOS5) */ 506 507 #ifdef LINEMODE 508 /* 509 * defer_terminit 510 * 511 * Some things should not be done until after the login process has started 512 * and all the pty modes are set to what they are supposed to be. This 513 * function is called when the pty state has been processed for the first time. 514 * It calls other functions that do things that were deferred in each module. 515 */ 516 defer_terminit() 517 { 518 519 /* 520 * local stuff that got deferred. 521 */ 522 if (def_tspeed != -1) { 523 clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed); 524 def_tspeed = def_rspeed = 0; 525 } 526 527 #ifdef TIOCSWINSZ 528 if (def_col || def_row) { 529 struct winsize ws; 530 531 ws.ws_col = def_col; 532 ws.ws_row = def_row; 533 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); 534 } 535 #endif 536 537 /* 538 * The only other module that currently defers anything. 539 */ 540 deferslc(); 541 542 } /* end of defer_terminit */ 543 544 /* 545 * terminit 546 * 547 * Returns true if the pty state has been processed yet. 548 */ 549 int terminit() 550 { 551 return _terminit; 552 553 } /* end of terminit */ 554 #endif /* LINEMODE */ 555