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