prompt.c revision 37011
1290001Sglebius/*- 2290001Sglebius * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> 3290001Sglebius * All rights reserved. 4290001Sglebius * 5290001Sglebius * Redistribution and use in source and binary forms, with or without 6290001Sglebius * modification, are permitted provided that the following conditions 7290001Sglebius * are met: 8290001Sglebius * 1. Redistributions of source code must retain the above copyright 9290001Sglebius * notice, this list of conditions and the following disclaimer. 10290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11290001Sglebius * notice, this list of conditions and the following disclaimer in the 12290001Sglebius * documentation and/or other materials provided with the distribution. 13290001Sglebius * 14290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15290001Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16290001Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17290001Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: prompt.c,v 1.5 1998/06/15 19:06:53 brian Exp $ 27 */ 28 29#include <sys/param.h> 30#include <netinet/in.h> 31#include <netinet/in_systm.h> 32#include <netinet/ip.h> 33#include <sys/un.h> 34 35#include <errno.h> 36#include <stdarg.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <sys/fcntl.h> 41#include <termios.h> 42#include <unistd.h> 43 44#include "defs.h" 45#include "timer.h" 46#include "command.h" 47#include "log.h" 48#include "descriptor.h" 49#include "prompt.h" 50#include "fsm.h" 51#include "lcp.h" 52#include "auth.h" 53#include "iplist.h" 54#include "throughput.h" 55#include "slcompress.h" 56#include "ipcp.h" 57#include "filter.h" 58#include "lqr.h" 59#include "hdlc.h" 60#include "async.h" 61#include "mbuf.h" 62#include "ccp.h" 63#include "link.h" 64#include "physical.h" 65#include "mp.h" 66#include "bundle.h" 67#include "chat.h" 68#include "chap.h" 69#include "datalink.h" 70#include "server.h" 71 72static void 73prompt_Display(struct prompt *p) 74{ 75 /* XXX: See Index2Nam() - should we only figure this out once ? */ 76 static char shostname[MAXHOSTNAMELEN]; 77 const char *pconnect, *pauth; 78 79 if (p->TermMode || !p->needprompt) 80 return; 81 82 p->needprompt = 0; 83 84 if (p->nonewline) 85 p->nonewline = 0; 86 else 87 fprintf(p->Term, "\n"); 88 89 if (p->auth == LOCAL_AUTH) 90 pauth = " ON "; 91 else 92 pauth = " on "; 93 94 if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED) 95 pconnect = "PPP"; 96 else if (bundle_Phase(p->bundle) == PHASE_NETWORK) 97 pconnect = "PPp"; 98 else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE) 99 pconnect = "Ppp"; 100 else 101 pconnect = "ppp"; 102 103 if (*shostname == '\0') { 104 char *dot; 105 106 if (gethostname(shostname, sizeof shostname)) 107 strcpy(shostname, "localhost"); 108 else if ((dot = strchr(shostname, '.'))) 109 *dot = '\0'; 110 } 111 112 fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname); 113 fflush(p->Term); 114} 115 116static int 117prompt_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 118{ 119 struct prompt *p = descriptor2prompt(d); 120 int sets; 121 122 sets = 0; 123 124 if (!p->active) 125 return sets; 126 127 if (p->fd_in >= 0) { 128 if (r) { 129 FD_SET(p->fd_in, r); 130 log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in); 131 sets++; 132 } 133 if (e) { 134 FD_SET(p->fd_in, e); 135 log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in); 136 sets++; 137 } 138 if (sets && *n < p->fd_in + 1) 139 *n = p->fd_in + 1; 140 } 141 142 prompt_Display(p); 143 144 return sets; 145} 146 147static int 148prompt_IsSet(struct descriptor *d, const fd_set *fdset) 149{ 150 struct prompt *p = descriptor2prompt(d); 151 return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset); 152} 153 154 155static void 156prompt_ShowHelp(struct prompt *p) 157{ 158 prompt_Printf(p, "The following commands are available:\n"); 159 prompt_Printf(p, " ~p\tEnter Packet mode\n"); 160 prompt_Printf(p, " ~t\tShow timers\n"); 161 prompt_Printf(p, " ~m\tShow memory map\n"); 162 prompt_Printf(p, " ~.\tTerminate program\n"); 163 prompt_Printf(p, " ~?\tThis help\n"); 164} 165 166static void 167prompt_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 168{ 169 struct prompt *p = descriptor2prompt(d); 170 int n; 171 char ch; 172 char linebuff[LINE_LEN]; 173 174 if (p->TermMode == NULL) { 175 n = read(p->fd_in, linebuff, sizeof linebuff - 1); 176 if (n > 0) { 177 if (linebuff[n-1] == '\n') 178 linebuff[--n] = '\0'; 179 else 180 linebuff[n] = '\0'; 181 p->nonewline = 1; /* Maybe command_Decode does a prompt */ 182 prompt_Required(p); 183 if (n) 184 command_Decode(bundle, linebuff, n, p, p->src.from); 185 } else if (n <= 0) { 186 log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from); 187 prompt_Destroy(p, 0); 188 } 189 return; 190 } 191 192 switch (p->TermMode->state) { 193 case DATALINK_CLOSED: 194 prompt_Printf(p, "Link lost, terminal mode.\n"); 195 prompt_TtyCommandMode(p); 196 p->nonewline = 0; 197 prompt_Required(p); 198 return; 199 200 case DATALINK_READY: 201 break; 202 203 case DATALINK_OPEN: 204 prompt_Printf(p, "\nPacket mode detected.\n"); 205 prompt_TtyCommandMode(p); 206 p->nonewline = 0; 207 /* We'll get a prompt because of our status change */ 208 /* Fall through */ 209 210 default: 211 /* Wait 'till we're in a state we care about */ 212 return; 213 } 214 215 /* 216 * We are in terminal mode, decode special sequences 217 */ 218 n = read(p->fd_in, &ch, 1); 219 log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 220 221 if (n > 0) { 222 switch (p->readtilde) { 223 case 0: 224 if (ch == '~') 225 p->readtilde = 1; 226 else 227 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 228 log_Printf(LogERROR, "error writing to modem: %s\n", strerror(errno)); 229 prompt_TtyCommandMode(p); 230 } 231 break; 232 case 1: 233 switch (ch) { 234 case '?': 235 prompt_ShowHelp(p); 236 break; 237 case 'p': 238 datalink_Up(p->TermMode, 0, 1); 239 prompt_Printf(p, "\nPacket mode.\n"); 240 prompt_TtyCommandMode(p); 241 break; 242 case '.': 243 prompt_TtyCommandMode(p); 244 p->nonewline = 0; 245 prompt_Required(p); 246 break; 247 case 't': 248 timer_Show(0, p); 249 break; 250 case 'm': 251 { 252 struct cmdargs arg; 253 254 arg.cmdtab = NULL; 255 arg.cmd = NULL; 256 arg.argc = 0; 257 arg.argn = 0; 258 arg.argv = NULL; 259 arg.bundle = bundle; 260 arg.cx = p->TermMode; 261 arg.prompt = p; 262 263 mbuf_Show(&arg); 264 } 265 break; 266 default: 267 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 268 log_Printf(LogERROR, "error writing to modem: %s\n", strerror(errno)); 269 prompt_TtyCommandMode(p); 270 } 271 break; 272 } 273 p->readtilde = 0; 274 break; 275 } 276 } 277} 278 279static void 280prompt_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 281{ 282 /* We never want to write here ! */ 283 log_Printf(LogERROR, "prompt_Write: Internal error: Bad call !\n"); 284} 285 286struct prompt * 287prompt_Create(struct server *s, struct bundle *bundle, int fd) 288{ 289 struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt)); 290 291 if (p != NULL) { 292 p->desc.type = PROMPT_DESCRIPTOR; 293 p->desc.UpdateSet = prompt_UpdateSet; 294 p->desc.IsSet = prompt_IsSet; 295 p->desc.Read = prompt_Read; 296 p->desc.Write = prompt_Write; 297 298 if (fd == PROMPT_STD) { 299 char *tty = ttyname(STDIN_FILENO); 300 301 if (!tty) { 302 free(p); 303 return NULL; 304 } 305 p->fd_in = STDIN_FILENO; 306 p->fd_out = STDOUT_FILENO; 307 p->Term = stdout; 308 p->owner = NULL; 309 p->auth = LOCAL_AUTH; 310 p->src.type = "Controller"; 311 strncpy(p->src.from, tty, sizeof p->src.from - 1); 312 p->src.from[sizeof p->src.from - 1] = '\0'; 313 tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */ 314 } else { 315 p->fd_in = p->fd_out = fd; 316 p->Term = fdopen(fd, "a+"); 317 p->owner = s; 318 p->auth = *s->passwd ? LOCAL_NO_AUTH : LOCAL_AUTH; 319 p->src.type = "unknown"; 320 *p->src.from = '\0'; 321 } 322 p->TermMode = NULL; 323 p->nonewline = 1; 324 p->needprompt = 1; 325 p->readtilde = 0; 326 p->bundle = bundle; 327 log_RegisterPrompt(p); 328 } 329 330 return p; 331} 332 333void 334prompt_Destroy(struct prompt *p, int verbose) 335{ 336 if (p) { 337 if (p->Term != stdout) { 338 fclose(p->Term); 339 close(p->fd_in); 340 if (p->fd_out != p->fd_in) 341 close(p->fd_out); 342 if (verbose) 343 log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from); 344 } else 345 prompt_TtyOldMode(p); 346 347 log_UnRegisterPrompt(p); 348 free(p); 349 } 350} 351 352void 353prompt_Printf(struct prompt *p, const char *fmt,...) 354{ 355 if (p && p->active) { 356 va_list ap; 357 358 va_start(ap, fmt); 359 prompt_vPrintf(p, fmt, ap); 360 va_end(ap); 361 } 362} 363 364void 365prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap) 366{ 367 if (p && p->active) { 368 char nfmt[LINE_LEN]; 369 const char *pfmt; 370 371 if (p->TermMode) { 372 /* Stuff '\r' in front of '\n' 'cos we're in raw mode */ 373 int len = strlen(fmt); 374 375 if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n') { 376 strcpy(nfmt, fmt); 377 strcpy(nfmt + len - 1, "\r\n"); 378 pfmt = nfmt; 379 } else 380 pfmt = fmt; 381 } else 382 pfmt = fmt; 383 vfprintf(p->Term, pfmt, ap); 384 fflush(p->Term); 385 p->nonewline = 1; 386 } 387} 388 389void 390prompt_TtyInit(struct prompt *p) 391{ 392 int stat, fd = p ? p->fd_in : STDIN_FILENO; 393 struct termios newtio; 394 395 stat = fcntl(fd, F_GETFL, 0); 396 if (stat > 0) { 397 stat |= O_NONBLOCK; 398 fcntl(fd, F_SETFL, stat); 399 } 400 401 if (p) 402 newtio = p->oldtio; 403 else 404 tcgetattr(fd, &newtio); 405 406 newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 407 newtio.c_iflag = 0; 408 newtio.c_oflag &= ~OPOST; 409 newtio.c_cc[VEOF] = _POSIX_VDISABLE; 410 if (!p) 411 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 412 newtio.c_cc[VMIN] = 1; 413 newtio.c_cc[VTIME] = 0; 414 newtio.c_cflag |= CS8; 415 tcsetattr(fd, TCSANOW, &newtio); 416 if (p) 417 p->comtio = newtio; 418} 419 420/* 421 * Set tty into command mode. We allow canonical input and echo processing. 422 */ 423void 424prompt_TtyCommandMode(struct prompt *p) 425{ 426 struct termios newtio; 427 int stat; 428 429 tcgetattr(p->fd_in, &newtio); 430 newtio.c_lflag |= (ECHO | ISIG | ICANON); 431 newtio.c_iflag = p->oldtio.c_iflag; 432 newtio.c_oflag |= OPOST; 433 tcsetattr(p->fd_in, TCSADRAIN, &newtio); 434 435 stat = fcntl(p->fd_in, F_GETFL, 0); 436 if (stat > 0) { 437 stat |= O_NONBLOCK; 438 fcntl(p->fd_in, F_SETFL, stat); 439 } 440 441 p->TermMode = NULL; 442} 443 444/* 445 * Set tty into terminal mode which is used while we invoke term command. 446 */ 447void 448prompt_TtyTermMode(struct prompt *p, struct datalink *dl) 449{ 450 int stat; 451 452 prompt_Printf(p, "Entering terminal mode on %s.\n", dl->name); 453 prompt_Printf(p, "Type `~?' for help.\n"); 454 455 if (p->Term == stdout) 456 tcsetattr(p->fd_in, TCSADRAIN, &p->comtio); 457 458 stat = fcntl(p->fd_in, F_GETFL, 0); 459 if (stat > 0) { 460 stat &= ~O_NONBLOCK; 461 fcntl(p->fd_in, F_SETFL, stat); 462 } 463 p->TermMode = dl; 464} 465 466void 467prompt_TtyOldMode(struct prompt *p) 468{ 469 int stat; 470 471 stat = fcntl(p->fd_in, F_GETFL, 0); 472 if (stat > 0) { 473 stat &= ~O_NONBLOCK; 474 fcntl(p->fd_in, F_SETFL, stat); 475 } 476 477 if (p->Term == stdout) 478 tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio); 479} 480 481pid_t 482prompt_pgrp(struct prompt *p) 483{ 484 return tcgetpgrp(p->fd_in); 485} 486 487int 488PasswdCommand(struct cmdargs const *arg) 489{ 490 const char *pass; 491 492 if (!arg->prompt) { 493 log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n"); 494 return 0; 495 } 496 497 if (arg->prompt->owner == NULL) { 498 log_Printf(LogWARN, "passwd: Not required\n"); 499 return 0; 500 } 501 502 if (arg->argc == arg->argn) 503 pass = ""; 504 else if (arg->argc > arg->argn+1) 505 return -1; 506 else 507 pass = arg->argv[arg->argn]; 508 509 if (!strcmp(arg->prompt->owner->passwd, pass)) 510 arg->prompt->auth = LOCAL_AUTH; 511 else 512 arg->prompt->auth = LOCAL_NO_AUTH; 513 514 return 0; 515} 516 517static struct pppTimer bgtimer; 518 519static void 520prompt_TimedContinue(void *v) 521{ 522 prompt_Continue((struct prompt *)v); 523} 524 525void 526prompt_Continue(struct prompt *p) 527{ 528 timer_Stop(&bgtimer); 529 if (getpgrp() == prompt_pgrp(p)) { 530 prompt_TtyCommandMode(p); 531 p->nonewline = 1; 532 prompt_Required(p); 533 log_ActivatePrompt(p); 534 } else if (!p->owner) { 535 bgtimer.func = prompt_TimedContinue; 536 bgtimer.name = "prompt bg"; 537 bgtimer.load = SECTICKS; 538 bgtimer.arg = p; 539 timer_Start(&bgtimer); 540 } 541} 542 543void 544prompt_Suspend(struct prompt *p) 545{ 546 if (getpgrp() == prompt_pgrp(p)) { 547 prompt_TtyOldMode(p); 548 log_DeactivatePrompt(p); 549 } 550} 551