prompt.c revision 46686
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> 31590Srgrimes * All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 141590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241590Srgrimes * SUCH DAMAGE. 251590Srgrimes * 261590Srgrimes * $Id: prompt.c,v 1.14 1999/04/03 11:54:00 brian Exp $ 271590Srgrimes */ 281590Srgrimes 291590Srgrimes#include <sys/param.h> 301590Srgrimes#include <netinet/in.h> 311590Srgrimes#include <netinet/in_systm.h> 321590Srgrimes#include <netinet/ip.h> 331590Srgrimes#include <sys/un.h> 341590Srgrimes 3593086Smarkm#include <errno.h> 361590Srgrimes#include <stdarg.h> 371590Srgrimes#include <stdio.h> 3893086Smarkm#include <stdlib.h> 3993086Smarkm#include <string.h> 4093086Smarkm#include <sys/fcntl.h> 411590Srgrimes#include <termios.h> 421590Srgrimes#include <unistd.h> 431590Srgrimes 441590Srgrimes#include "layer.h" 451590Srgrimes#include "defs.h" 461590Srgrimes#include "timer.h" 471590Srgrimes#include "command.h" 481590Srgrimes#include "log.h" 491590Srgrimes#include "descriptor.h" 501590Srgrimes#include "prompt.h" 511590Srgrimes#include "fsm.h" 521590Srgrimes#include "lcp.h" 531590Srgrimes#include "auth.h" 5493086Smarkm#include "iplist.h" 551590Srgrimes#include "throughput.h" 561590Srgrimes#include "slcompress.h" 571590Srgrimes#include "mbuf.h" 581590Srgrimes#include "lqr.h" 591590Srgrimes#include "hdlc.h" 6093086Smarkm#include "ipcp.h" 611590Srgrimes#include "filter.h" 621590Srgrimes#include "async.h" 631590Srgrimes#include "ccp.h" 641590Srgrimes#include "link.h" 651590Srgrimes#include "physical.h" 661590Srgrimes#include "mp.h" 671590Srgrimes#ifndef NORADIUS 689987Swollman#include "radius.h" 691590Srgrimes#endif 701590Srgrimes#include "bundle.h" 711590Srgrimes#include "chat.h" 721590Srgrimes#include "chap.h" 738874Srgrimes#include "cbcp.h" 741590Srgrimes#include "datalink.h" 751590Srgrimes#include "server.h" 7693086Smarkm#include "main.h" 771590Srgrimes 781590Srgrimesstatic void 7993086Smarkmprompt_Display(struct prompt *p) 8093086Smarkm{ 811590Srgrimes /* XXX: See Index2Nam() - should we only figure this out once ? */ 821590Srgrimes static char shostname[MAXHOSTNAMELEN]; 831590Srgrimes const char *pconnect, *pauth; 841590Srgrimes 851590Srgrimes if (p->TermMode || !p->needprompt) 861590Srgrimes return; 871590Srgrimes 881590Srgrimes p->needprompt = 0; 891590Srgrimes 901590Srgrimes if (p->nonewline) 911590Srgrimes p->nonewline = 0; 921590Srgrimes else 931590Srgrimes fprintf(p->Term, "\n"); 941590Srgrimes 9517544Speter if (p->auth == LOCAL_AUTH) 9617544Speter pauth = " ON "; 9717544Speter else 9817544Speter pauth = " on "; 9917544Speter 10017544Speter if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED) 10117544Speter pconnect = "PPP"; 10217544Speter else if (bundle_Phase(p->bundle) == PHASE_NETWORK) 10317544Speter pconnect = "PPp"; 10417544Speter else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE) 1051590Srgrimes pconnect = "Ppp"; 1061590Srgrimes else 1071590Srgrimes pconnect = "ppp"; 1081590Srgrimes 1091590Srgrimes if (*shostname == '\0') { 1101590Srgrimes char *dot; 1111590Srgrimes 1121590Srgrimes if (gethostname(shostname, sizeof shostname)) 11392554Scjc strcpy(shostname, "localhost"); 1141590Srgrimes else if ((dot = strchr(shostname, '.'))) 11542815Sdanny *dot = '\0'; 11642815Sdanny } 11742815Sdanny 11842815Sdanny fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname); 1199987Swollman fflush(p->Term); 12042815Sdanny} 1211590Srgrimes 1229987Swollmanstatic int 1239554Smppprompt_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 1249554Smpp{ 1259554Smpp struct prompt *p = descriptor2prompt(d); 1269554Smpp int sets; 1279554Smpp 1289554Smpp sets = 0; 1299554Smpp 1309554Smpp if (!p->active) 1311590Srgrimes return sets; 1321590Srgrimes 1331590Srgrimes if (p->fd_in >= 0) { 1341590Srgrimes if (r) { 1351590Srgrimes FD_SET(p->fd_in, r); 13693086Smarkm log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in); 1371590Srgrimes sets++; 1381590Srgrimes } 1391590Srgrimes if (e) { 1401590Srgrimes FD_SET(p->fd_in, e); 14134797Scharnier log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in); 1421590Srgrimes sets++; 1431590Srgrimes } 1441590Srgrimes if (sets && *n < p->fd_in + 1) 1451590Srgrimes *n = p->fd_in + 1; 1461590Srgrimes } 1471590Srgrimes 1481590Srgrimes prompt_Display(p); 1491590Srgrimes 150 return sets; 151} 152 153static int 154prompt_IsSet(struct descriptor *d, const fd_set *fdset) 155{ 156 struct prompt *p = descriptor2prompt(d); 157 return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset); 158} 159 160 161static void 162prompt_ShowHelp(struct prompt *p) 163{ 164 prompt_Printf(p, "The following commands are available:\n"); 165 prompt_Printf(p, " ~p\tEnter Packet mode\n"); 166 prompt_Printf(p, " ~t\tShow timers\n"); 167 prompt_Printf(p, " ~m\tShow memory map\n"); 168 prompt_Printf(p, " ~.\tTerminate program\n"); 169 prompt_Printf(p, " ~?\tThis help\n"); 170} 171 172static void 173prompt_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 174{ 175 struct prompt *p = descriptor2prompt(d); 176 int n; 177 char ch; 178 char linebuff[LINE_LEN]; 179 180 if (p->TermMode == NULL) { 181 n = read(p->fd_in, linebuff, sizeof linebuff - 1); 182 if (n > 0) { 183 if (linebuff[n-1] == '\n') 184 linebuff[--n] = '\0'; 185 else 186 linebuff[n] = '\0'; 187 p->nonewline = 1; /* Maybe command_Decode does a prompt */ 188 prompt_Required(p); 189 if (n) 190 command_Decode(bundle, linebuff, n, p, p->src.from); 191 } else if (n <= 0) { 192 log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from); 193 if (!p->owner) 194 Cleanup(EX_NORMAL); 195 prompt_Destroy(p, 0); 196 } 197 return; 198 } 199 200 switch (p->TermMode->state) { 201 case DATALINK_CLOSED: 202 prompt_Printf(p, "Link lost, terminal mode.\n"); 203 prompt_TtyCommandMode(p); 204 p->nonewline = 0; 205 prompt_Required(p); 206 return; 207 208 case DATALINK_READY: 209 break; 210 211 case DATALINK_OPEN: 212 prompt_Printf(p, "\nPacket mode detected.\n"); 213 prompt_TtyCommandMode(p); 214 p->nonewline = 0; 215 /* We'll get a prompt because of our status change */ 216 /* Fall through */ 217 218 default: 219 /* Wait 'till we're in a state we care about */ 220 return; 221 } 222 223 /* 224 * We are in terminal mode, decode special sequences 225 */ 226 n = read(p->fd_in, &ch, 1); 227 log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 228 229 if (n > 0) { 230 switch (p->readtilde) { 231 case 0: 232 if (ch == '~') 233 p->readtilde = 1; 234 else 235 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 236 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 237 prompt_TtyCommandMode(p); 238 } 239 break; 240 case 1: 241 switch (ch) { 242 case '?': 243 prompt_ShowHelp(p); 244 break; 245 case 'p': 246 datalink_Up(p->TermMode, 0, 1); 247 prompt_Printf(p, "\nPacket mode.\n"); 248 prompt_TtyCommandMode(p); 249 break; 250 case '.': 251 prompt_TtyCommandMode(p); 252 p->nonewline = 0; 253 prompt_Required(p); 254 break; 255 case 't': 256 timer_Show(0, p); 257 break; 258 case 'm': 259 { 260 struct cmdargs arg; 261 262 arg.cmdtab = NULL; 263 arg.cmd = NULL; 264 arg.argc = 0; 265 arg.argn = 0; 266 arg.argv = NULL; 267 arg.bundle = bundle; 268 arg.cx = p->TermMode; 269 arg.prompt = p; 270 271 mbuf_Show(&arg); 272 } 273 break; 274 default: 275 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 276 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 277 prompt_TtyCommandMode(p); 278 } 279 break; 280 } 281 p->readtilde = 0; 282 break; 283 } 284 } 285} 286 287static int 288prompt_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 289{ 290 /* We never want to write here ! */ 291 log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n"); 292 return 0; 293} 294 295struct prompt * 296prompt_Create(struct server *s, struct bundle *bundle, int fd) 297{ 298 struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt)); 299 300 if (p != NULL) { 301 p->desc.type = PROMPT_DESCRIPTOR; 302 p->desc.UpdateSet = prompt_UpdateSet; 303 p->desc.IsSet = prompt_IsSet; 304 p->desc.Read = prompt_Read; 305 p->desc.Write = prompt_Write; 306 307 if (fd == PROMPT_STD) { 308 char *tty = ttyname(STDIN_FILENO); 309 310 if (!tty) { 311 free(p); 312 return NULL; 313 } 314 p->fd_in = STDIN_FILENO; 315 p->fd_out = STDOUT_FILENO; 316 p->Term = stdout; 317 p->owner = NULL; 318 p->auth = LOCAL_AUTH; 319 p->src.type = "Controller"; 320 strncpy(p->src.from, tty, sizeof p->src.from - 1); 321 p->src.from[sizeof p->src.from - 1] = '\0'; 322 tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */ 323 } else { 324 p->fd_in = p->fd_out = fd; 325 p->Term = fdopen(fd, "a+"); 326 p->owner = s; 327 p->auth = *s->passwd ? LOCAL_NO_AUTH : LOCAL_AUTH; 328 p->src.type = "unknown"; 329 *p->src.from = '\0'; 330 } 331 p->TermMode = NULL; 332 p->nonewline = 1; 333 p->needprompt = 1; 334 p->readtilde = 0; 335 p->bundle = bundle; 336 log_RegisterPrompt(p); 337 } 338 339 return p; 340} 341 342void 343prompt_Destroy(struct prompt *p, int verbose) 344{ 345 if (p) { 346 if (p->Term != stdout) { 347 fclose(p->Term); 348 close(p->fd_in); 349 if (p->fd_out != p->fd_in) 350 close(p->fd_out); 351 if (verbose) 352 log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from); 353 } else 354 prompt_TtyOldMode(p); 355 356 log_UnRegisterPrompt(p); 357 free(p); 358 } 359} 360 361void 362prompt_Printf(struct prompt *p, const char *fmt,...) 363{ 364 if (p && p->active) { 365 va_list ap; 366 367 va_start(ap, fmt); 368 prompt_vPrintf(p, fmt, ap); 369 va_end(ap); 370 } 371} 372 373void 374prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap) 375{ 376 if (p && p->active) { 377 char nfmt[LINE_LEN]; 378 const char *pfmt; 379 380 if (p->TermMode) { 381 /* Stuff '\r' in front of '\n' 'cos we're in raw mode */ 382 int len = strlen(fmt); 383 384 if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' && 385 (len == 1 || fmt[len-2] != '\r')) { 386 strcpy(nfmt, fmt); 387 strcpy(nfmt + len - 1, "\r\n"); 388 pfmt = nfmt; 389 } else 390 pfmt = fmt; 391 } else 392 pfmt = fmt; 393 vfprintf(p->Term, pfmt, ap); 394 fflush(p->Term); 395 p->nonewline = 1; 396 } 397} 398 399void 400prompt_TtyInit(struct prompt *p) 401{ 402 int stat, fd = p ? p->fd_in : STDIN_FILENO; 403 struct termios newtio; 404 405 stat = fcntl(fd, F_GETFL, 0); 406 if (stat > 0) { 407 stat |= O_NONBLOCK; 408 fcntl(fd, F_SETFL, stat); 409 } 410 411 if (p) 412 newtio = p->oldtio; 413 else 414 tcgetattr(fd, &newtio); 415 416 newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 417 newtio.c_iflag = 0; 418 newtio.c_oflag &= ~OPOST; 419 if (!p) 420 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 421 newtio.c_cc[VMIN] = 1; 422 newtio.c_cc[VTIME] = 0; 423 newtio.c_cflag |= CS8; 424 tcsetattr(fd, TCSANOW, &newtio); 425 if (p) 426 p->comtio = newtio; 427} 428 429/* 430 * Set tty into command mode. We allow canonical input and echo processing. 431 */ 432void 433prompt_TtyCommandMode(struct prompt *p) 434{ 435 struct termios newtio; 436 int stat; 437 438 tcgetattr(p->fd_in, &newtio); 439 newtio.c_lflag |= (ECHO | ISIG | ICANON); 440 newtio.c_iflag = p->oldtio.c_iflag; 441 newtio.c_oflag |= OPOST; 442 tcsetattr(p->fd_in, TCSADRAIN, &newtio); 443 444 stat = fcntl(p->fd_in, F_GETFL, 0); 445 if (stat > 0) { 446 stat |= O_NONBLOCK; 447 fcntl(p->fd_in, F_SETFL, stat); 448 } 449 450 p->TermMode = NULL; 451} 452 453/* 454 * Set tty into terminal mode which is used while we invoke term command. 455 */ 456void 457prompt_TtyTermMode(struct prompt *p, struct datalink *dl) 458{ 459 int stat; 460 461 if (p->Term == stdout) 462 tcsetattr(p->fd_in, TCSADRAIN, &p->comtio); 463 464 stat = fcntl(p->fd_in, F_GETFL, 0); 465 if (stat > 0) { 466 stat &= ~O_NONBLOCK; 467 fcntl(p->fd_in, F_SETFL, stat); 468 } 469 p->TermMode = dl; 470} 471 472void 473prompt_TtyOldMode(struct prompt *p) 474{ 475 int stat; 476 477 stat = fcntl(p->fd_in, F_GETFL, 0); 478 if (stat > 0) { 479 stat &= ~O_NONBLOCK; 480 fcntl(p->fd_in, F_SETFL, stat); 481 } 482 483 if (p->Term == stdout) 484 tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio); 485} 486 487pid_t 488prompt_pgrp(struct prompt *p) 489{ 490 return tcgetpgrp(p->fd_in); 491} 492 493int 494PasswdCommand(struct cmdargs const *arg) 495{ 496 const char *pass; 497 498 if (!arg->prompt) { 499 log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n"); 500 return 0; 501 } 502 503 if (arg->prompt->owner == NULL) { 504 log_Printf(LogWARN, "passwd: Not required\n"); 505 return 0; 506 } 507 508 if (arg->argc == arg->argn) 509 pass = ""; 510 else if (arg->argc > arg->argn+1) 511 return -1; 512 else 513 pass = arg->argv[arg->argn]; 514 515 if (!strcmp(arg->prompt->owner->passwd, pass)) 516 arg->prompt->auth = LOCAL_AUTH; 517 else 518 arg->prompt->auth = LOCAL_NO_AUTH; 519 520 return 0; 521} 522 523static struct pppTimer bgtimer; 524 525static void 526prompt_TimedContinue(void *v) 527{ 528 prompt_Continue((struct prompt *)v); 529} 530 531void 532prompt_Continue(struct prompt *p) 533{ 534 timer_Stop(&bgtimer); 535 if (getpgrp() == prompt_pgrp(p)) { 536 prompt_TtyCommandMode(p); 537 p->nonewline = 1; 538 prompt_Required(p); 539 log_ActivatePrompt(p); 540 } else if (!p->owner) { 541 bgtimer.func = prompt_TimedContinue; 542 bgtimer.name = "prompt bg"; 543 bgtimer.load = SECTICKS; 544 bgtimer.arg = p; 545 timer_Start(&bgtimer); 546 } 547} 548 549void 550prompt_Suspend(struct prompt *p) 551{ 552 if (getpgrp() == prompt_pgrp(p)) { 553 prompt_TtyOldMode(p); 554 log_DeactivatePrompt(p); 555 } 556} 557