prompt.c revision 36431
1/*- 2 * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * 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.3 1998/05/23 22:24:48 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 static char shostname[MAXHOSTNAMELEN]; 76 const char *pconnect, *pauth; 77 78 if (p->TermMode || !p->needprompt) 79 return; 80 81 p->needprompt = 0; 82 83 if (p->nonewline) 84 p->nonewline = 0; 85 else 86 fprintf(p->Term, "\n"); 87 88 if (p->auth == LOCAL_AUTH) 89 pauth = " ON "; 90 else 91 pauth = " on "; 92 93 if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED) 94 pconnect = "PPP"; 95 else if (bundle_Phase(p->bundle) == PHASE_NETWORK) 96 pconnect = "PPp"; 97 else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE) 98 pconnect = "Ppp"; 99 else 100 pconnect = "ppp"; 101 102 if (*shostname == '\0') { 103 char *dot; 104 105 if (gethostname(shostname, sizeof shostname)) 106 strcpy(shostname, "localhost"); 107 else if ((dot = strchr(shostname, '.'))) 108 *dot = '\0'; 109 } 110 111 fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname); 112 fflush(p->Term); 113} 114 115static int 116prompt_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 117{ 118 struct prompt *p = descriptor2prompt(d); 119 int sets; 120 121 sets = 0; 122 123 if (!p->active) 124 return sets; 125 126 if (p->fd_in >= 0) { 127 if (r) { 128 FD_SET(p->fd_in, r); 129 log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in); 130 sets++; 131 } 132 if (e) { 133 FD_SET(p->fd_in, e); 134 log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in); 135 sets++; 136 } 137 if (sets && *n < p->fd_in + 1) 138 *n = p->fd_in + 1; 139 } 140 141 prompt_Display(p); 142 143 return sets; 144} 145 146static int 147prompt_IsSet(struct descriptor *d, const fd_set *fdset) 148{ 149 struct prompt *p = descriptor2prompt(d); 150 return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset); 151} 152 153 154static void 155prompt_ShowHelp(struct prompt *p) 156{ 157 prompt_Printf(p, "The following commands are available:\r\n"); 158 prompt_Printf(p, " ~p\tEnter Packet mode\r\n"); 159 prompt_Printf(p, " ~-\tDecrease log level\r\n"); 160 prompt_Printf(p, " ~+\tIncrease log level\r\n"); 161 prompt_Printf(p, " ~t\tShow timers\r\n"); 162 prompt_Printf(p, " ~m\tShow memory map\r\n"); 163 prompt_Printf(p, " ~.\tTerminate program\r\n"); 164 prompt_Printf(p, " ~?\tThis help\r\n"); 165} 166 167static void 168prompt_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 169{ 170 struct prompt *p = descriptor2prompt(d); 171 int n; 172 char ch; 173 static int ttystate; 174 char linebuff[LINE_LEN]; 175 176 if (p->TermMode == NULL) { 177 n = read(p->fd_in, linebuff, sizeof linebuff - 1); 178 if (n > 0) { 179 if (linebuff[n-1] == '\n') 180 linebuff[--n] = '\0'; 181 else 182 linebuff[n] = '\0'; 183 p->nonewline = 1; /* Maybe command_Decode does a prompt */ 184 prompt_Required(p); 185 if (n) 186 command_Decode(bundle, linebuff, n, p, p->src.from); 187 } else if (n <= 0) { 188 log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from); 189 prompt_Destroy(p, 0); 190 } 191 return; 192 } 193 194 switch (p->TermMode->state) { 195 case DATALINK_CLOSED: 196 prompt_Printf(p, "Link lost, terminal mode.\n"); 197 prompt_TtyCommandMode(p); 198 p->nonewline = 0; 199 prompt_Required(p); 200 return; 201 202 case DATALINK_READY: 203 break; 204 205 case DATALINK_OPEN: 206 prompt_Printf(p, "\nPacket mode detected.\n"); 207 prompt_TtyCommandMode(p); 208 p->nonewline = 0; 209 /* We'll get a prompt because of our status change */ 210 /* Fall through */ 211 212 default: 213 /* Wait 'till we're in a state we care about */ 214 return; 215 } 216 217 /* 218 * We are in terminal mode, decode special sequences 219 */ 220 n = read(p->fd_in, &ch, 1); 221 log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 222 223 if (n > 0) { 224 switch (ttystate) { 225 case 0: 226 if (ch == '~') 227 ttystate++; 228 else 229 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 230 log_Printf(LogERROR, "error writing to modem: %s\n", strerror(errno)); 231 prompt_TtyCommandMode(p); 232 } 233 break; 234 case 1: 235 switch (ch) { 236 case '?': 237 prompt_ShowHelp(p); 238 break; 239 case 'p': 240 datalink_Up(p->TermMode, 0, 1); 241 prompt_Printf(p, "\nPacket mode.\n"); 242 prompt_TtyCommandMode(p); 243 break; 244 case '.': 245 prompt_TtyCommandMode(p); 246 p->nonewline = 0; 247 prompt_Required(p); 248 break; 249 case 't': 250 timer_Show(0, p); 251 break; 252 case 'm': 253 mbuf_Show(NULL); 254 break; 255 default: 256 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 257 log_Printf(LogERROR, "error writing to modem: %s\n", strerror(errno)); 258 prompt_TtyCommandMode(p); 259 } 260 break; 261 } 262 ttystate = 0; 263 break; 264 } 265 } 266} 267 268static void 269prompt_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 270{ 271 /* We never want to write here ! */ 272 log_Printf(LogERROR, "prompt_Write: Internal error: Bad call !\n"); 273} 274 275struct prompt * 276prompt_Create(struct server *s, struct bundle *bundle, int fd) 277{ 278 struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt)); 279 280 if (p != NULL) { 281 p->desc.type = PROMPT_DESCRIPTOR; 282 p->desc.UpdateSet = prompt_UpdateSet; 283 p->desc.IsSet = prompt_IsSet; 284 p->desc.Read = prompt_Read; 285 p->desc.Write = prompt_Write; 286 287 if (fd == PROMPT_STD) { 288 char *tty = ttyname(STDIN_FILENO); 289 290 if (!tty) { 291 free(p); 292 return NULL; 293 } 294 p->fd_in = STDIN_FILENO; 295 p->fd_out = STDOUT_FILENO; 296 p->Term = stdout; 297 p->owner = NULL; 298 p->auth = LOCAL_AUTH; 299 p->src.type = "Controller"; 300 strncpy(p->src.from, tty, sizeof p->src.from - 1); 301 p->src.from[sizeof p->src.from - 1] = '\0'; 302 tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */ 303 } else { 304 p->fd_in = p->fd_out = fd; 305 p->Term = fdopen(fd, "a+"); 306 p->owner = s; 307 p->auth = *s->passwd ? LOCAL_NO_AUTH : LOCAL_AUTH; 308 p->src.type = "unknown"; 309 *p->src.from = '\0'; 310 } 311 p->TermMode = NULL; 312 p->nonewline = 1; 313 p->needprompt = 1; 314 p->bundle = bundle; 315 log_RegisterPrompt(p); 316 } 317 318 return p; 319} 320 321void 322prompt_Destroy(struct prompt *p, int verbose) 323{ 324 if (p) { 325 if (p->Term != stdout) { 326 fclose(p->Term); 327 close(p->fd_in); 328 if (p->fd_out != p->fd_in) 329 close(p->fd_out); 330 if (verbose) 331 log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from); 332 } else 333 prompt_TtyOldMode(p); 334 335 log_UnRegisterPrompt(p); 336 free(p); 337 } 338} 339 340void 341prompt_Printf(struct prompt *p, const char *fmt,...) 342{ 343 if (p && p->active) { 344 va_list ap; 345 va_start(ap, fmt); 346 vfprintf(p->Term, fmt, ap); 347 fflush(p->Term); 348 va_end(ap); 349 p->nonewline = 1; 350 } 351} 352 353void 354prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap) 355{ 356 if (p && p->active) { 357 vfprintf(p->Term, fmt, ap); 358 fflush(p->Term); 359 p->nonewline = 1; 360 } 361} 362 363void 364prompt_TtyInit(struct prompt *p) 365{ 366 int stat, fd = p ? p->fd_in : STDIN_FILENO; 367 struct termios newtio; 368 369 stat = fcntl(fd, F_GETFL, 0); 370 if (stat > 0) { 371 stat |= O_NONBLOCK; 372 fcntl(fd, F_SETFL, stat); 373 } 374 375 if (p) 376 newtio = p->oldtio; 377 else 378 tcgetattr(fd, &newtio); 379 380 newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 381 newtio.c_iflag = 0; 382 newtio.c_oflag &= ~OPOST; 383 newtio.c_cc[VEOF] = _POSIX_VDISABLE; 384 if (!p) 385 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 386 newtio.c_cc[VMIN] = 1; 387 newtio.c_cc[VTIME] = 0; 388 newtio.c_cflag |= CS8; 389 tcsetattr(fd, TCSANOW, &newtio); 390 if (p) 391 p->comtio = newtio; 392} 393 394/* 395 * Set tty into command mode. We allow canonical input and echo processing. 396 */ 397void 398prompt_TtyCommandMode(struct prompt *p) 399{ 400 struct termios newtio; 401 int stat; 402 403 tcgetattr(p->fd_in, &newtio); 404 newtio.c_lflag |= (ECHO | ISIG | ICANON); 405 newtio.c_iflag = p->oldtio.c_iflag; 406 newtio.c_oflag |= OPOST; 407 tcsetattr(p->fd_in, TCSADRAIN, &newtio); 408 409 stat = fcntl(p->fd_in, F_GETFL, 0); 410 if (stat > 0) { 411 stat |= O_NONBLOCK; 412 fcntl(p->fd_in, F_SETFL, stat); 413 } 414 415 p->TermMode = NULL; 416} 417 418/* 419 * Set tty into terminal mode which is used while we invoke term command. 420 */ 421void 422prompt_TtyTermMode(struct prompt *p, struct datalink *dl) 423{ 424 int stat; 425 426 prompt_Printf(p, "Entering terminal mode on %s.\n", dl->name); 427 prompt_Printf(p, "Type `~?' for help.\n"); 428 429 if (p->Term == stdout) 430 tcsetattr(p->fd_in, TCSADRAIN, &p->comtio); 431 432 stat = fcntl(p->fd_in, F_GETFL, 0); 433 if (stat > 0) { 434 stat &= ~O_NONBLOCK; 435 fcntl(p->fd_in, F_SETFL, stat); 436 } 437 p->TermMode = dl; 438} 439 440void 441prompt_TtyOldMode(struct prompt *p) 442{ 443 int stat; 444 445 stat = fcntl(p->fd_in, F_GETFL, 0); 446 if (stat > 0) { 447 stat &= ~O_NONBLOCK; 448 fcntl(p->fd_in, F_SETFL, stat); 449 } 450 451 if (p->Term == stdout) 452 tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio); 453} 454 455pid_t 456prompt_pgrp(struct prompt *p) 457{ 458 return tcgetpgrp(p->fd_in); 459} 460 461int 462PasswdCommand(struct cmdargs const *arg) 463{ 464 const char *pass; 465 466 if (!arg->prompt) { 467 log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n"); 468 return 0; 469 } 470 471 if (arg->prompt->owner == NULL) { 472 log_Printf(LogWARN, "passwd: Not required\n"); 473 return 0; 474 } 475 476 if (arg->argc == arg->argn) 477 pass = ""; 478 else if (arg->argc > arg->argn+1) 479 return -1; 480 else 481 pass = arg->argv[arg->argn]; 482 483 if (!strcmp(arg->prompt->owner->passwd, pass)) 484 arg->prompt->auth = LOCAL_AUTH; 485 else 486 arg->prompt->auth = LOCAL_NO_AUTH; 487 488 return 0; 489} 490 491static struct pppTimer bgtimer; 492 493static void 494prompt_TimedContinue(void *v) 495{ 496 prompt_Continue((struct prompt *)v); 497} 498 499void 500prompt_Continue(struct prompt *p) 501{ 502 timer_Stop(&bgtimer); 503 if (getpgrp() == prompt_pgrp(p)) { 504 prompt_TtyCommandMode(p); 505 p->nonewline = 1; 506 prompt_Required(p); 507 log_ActivatePrompt(p); 508 } else if (!p->owner) { 509 bgtimer.func = prompt_TimedContinue; 510 bgtimer.name = "prompt bg"; 511 bgtimer.load = SECTICKS; 512 bgtimer.arg = p; 513 timer_Start(&bgtimer); 514 } 515} 516 517void 518prompt_Suspend(struct prompt *p) 519{ 520 if (getpgrp() == prompt_pgrp(p)) { 521 prompt_TtyOldMode(p); 522 log_DeactivatePrompt(p); 523 } 524} 525