prompt.c revision 38557
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.11 1998/08/09 15:34:11 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 "mbuf.h" 57#include "lqr.h" 58#include "hdlc.h" 59#include "ipcp.h" 60#include "filter.h" 61#include "async.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 "cbcp.h" 70#include "datalink.h" 71#include "server.h" 72#include "main.h" 73 74static void 75prompt_Display(struct prompt *p) 76{ 77 /* XXX: See Index2Nam() - should we only figure this out once ? */ 78 static char shostname[MAXHOSTNAMELEN]; 79 const char *pconnect, *pauth; 80 81 if (p->TermMode || !p->needprompt) 82 return; 83 84 p->needprompt = 0; 85 86 if (p->nonewline) 87 p->nonewline = 0; 88 else 89 fprintf(p->Term, "\n"); 90 91 if (p->auth == LOCAL_AUTH) 92 pauth = " ON "; 93 else 94 pauth = " on "; 95 96 if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED) 97 pconnect = "PPP"; 98 else if (bundle_Phase(p->bundle) == PHASE_NETWORK) 99 pconnect = "PPp"; 100 else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE) 101 pconnect = "Ppp"; 102 else 103 pconnect = "ppp"; 104 105 if (*shostname == '\0') { 106 char *dot; 107 108 if (gethostname(shostname, sizeof shostname)) 109 strcpy(shostname, "localhost"); 110 else if ((dot = strchr(shostname, '.'))) 111 *dot = '\0'; 112 } 113 114 fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname); 115 fflush(p->Term); 116} 117 118static int 119prompt_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 120{ 121 struct prompt *p = descriptor2prompt(d); 122 int sets; 123 124 sets = 0; 125 126 if (!p->active) 127 return sets; 128 129 if (p->fd_in >= 0) { 130 if (r) { 131 FD_SET(p->fd_in, r); 132 log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in); 133 sets++; 134 } 135 if (e) { 136 FD_SET(p->fd_in, e); 137 log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in); 138 sets++; 139 } 140 if (sets && *n < p->fd_in + 1) 141 *n = p->fd_in + 1; 142 } 143 144 prompt_Display(p); 145 146 return sets; 147} 148 149static int 150prompt_IsSet(struct descriptor *d, const fd_set *fdset) 151{ 152 struct prompt *p = descriptor2prompt(d); 153 return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset); 154} 155 156 157static void 158prompt_ShowHelp(struct prompt *p) 159{ 160 prompt_Printf(p, "The following commands are available:\n"); 161 prompt_Printf(p, " ~p\tEnter Packet mode\n"); 162 prompt_Printf(p, " ~t\tShow timers\n"); 163 prompt_Printf(p, " ~m\tShow memory map\n"); 164 prompt_Printf(p, " ~.\tTerminate program\n"); 165 prompt_Printf(p, " ~?\tThis help\n"); 166} 167 168static void 169prompt_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 170{ 171 struct prompt *p = descriptor2prompt(d); 172 int n; 173 char ch; 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 if (!p->owner) 190 Cleanup(EX_NORMAL); 191 prompt_Destroy(p, 0); 192 } 193 return; 194 } 195 196 switch (p->TermMode->state) { 197 case DATALINK_CLOSED: 198 prompt_Printf(p, "Link lost, terminal mode.\n"); 199 prompt_TtyCommandMode(p); 200 p->nonewline = 0; 201 prompt_Required(p); 202 return; 203 204 case DATALINK_READY: 205 break; 206 207 case DATALINK_OPEN: 208 prompt_Printf(p, "\nPacket mode detected.\n"); 209 prompt_TtyCommandMode(p); 210 p->nonewline = 0; 211 /* We'll get a prompt because of our status change */ 212 /* Fall through */ 213 214 default: 215 /* Wait 'till we're in a state we care about */ 216 return; 217 } 218 219 /* 220 * We are in terminal mode, decode special sequences 221 */ 222 n = read(p->fd_in, &ch, 1); 223 log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 224 225 if (n > 0) { 226 switch (p->readtilde) { 227 case 0: 228 if (ch == '~') 229 p->readtilde = 1; 230 else 231 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 232 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 233 prompt_TtyCommandMode(p); 234 } 235 break; 236 case 1: 237 switch (ch) { 238 case '?': 239 prompt_ShowHelp(p); 240 break; 241 case 'p': 242 datalink_Up(p->TermMode, 0, 1); 243 prompt_Printf(p, "\nPacket mode.\n"); 244 prompt_TtyCommandMode(p); 245 break; 246 case '.': 247 prompt_TtyCommandMode(p); 248 p->nonewline = 0; 249 prompt_Required(p); 250 break; 251 case 't': 252 timer_Show(0, p); 253 break; 254 case 'm': 255 { 256 struct cmdargs arg; 257 258 arg.cmdtab = NULL; 259 arg.cmd = NULL; 260 arg.argc = 0; 261 arg.argn = 0; 262 arg.argv = NULL; 263 arg.bundle = bundle; 264 arg.cx = p->TermMode; 265 arg.prompt = p; 266 267 mbuf_Show(&arg); 268 } 269 break; 270 default: 271 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 272 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 273 prompt_TtyCommandMode(p); 274 } 275 break; 276 } 277 p->readtilde = 0; 278 break; 279 } 280 } 281} 282 283static int 284prompt_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 285{ 286 /* We never want to write here ! */ 287 log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n"); 288 return 0; 289} 290 291struct prompt * 292prompt_Create(struct server *s, struct bundle *bundle, int fd) 293{ 294 struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt)); 295 296 if (p != NULL) { 297 p->desc.type = PROMPT_DESCRIPTOR; 298 p->desc.UpdateSet = prompt_UpdateSet; 299 p->desc.IsSet = prompt_IsSet; 300 p->desc.Read = prompt_Read; 301 p->desc.Write = prompt_Write; 302 303 if (fd == PROMPT_STD) { 304 char *tty = ttyname(STDIN_FILENO); 305 306 if (!tty) { 307 free(p); 308 return NULL; 309 } 310 p->fd_in = STDIN_FILENO; 311 p->fd_out = STDOUT_FILENO; 312 p->Term = stdout; 313 p->owner = NULL; 314 p->auth = LOCAL_AUTH; 315 p->src.type = "Controller"; 316 strncpy(p->src.from, tty, sizeof p->src.from - 1); 317 p->src.from[sizeof p->src.from - 1] = '\0'; 318 tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */ 319 } else { 320 p->fd_in = p->fd_out = fd; 321 p->Term = fdopen(fd, "a+"); 322 p->owner = s; 323 p->auth = *s->passwd ? LOCAL_NO_AUTH : LOCAL_AUTH; 324 p->src.type = "unknown"; 325 *p->src.from = '\0'; 326 } 327 p->TermMode = NULL; 328 p->nonewline = 1; 329 p->needprompt = 1; 330 p->readtilde = 0; 331 p->bundle = bundle; 332 log_RegisterPrompt(p); 333 } 334 335 return p; 336} 337 338void 339prompt_Destroy(struct prompt *p, int verbose) 340{ 341 if (p) { 342 if (p->Term != stdout) { 343 fclose(p->Term); 344 close(p->fd_in); 345 if (p->fd_out != p->fd_in) 346 close(p->fd_out); 347 if (verbose) 348 log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from); 349 } else 350 prompt_TtyOldMode(p); 351 352 log_UnRegisterPrompt(p); 353 free(p); 354 } 355} 356 357void 358prompt_Printf(struct prompt *p, const char *fmt,...) 359{ 360 if (p && p->active) { 361 va_list ap; 362 363 va_start(ap, fmt); 364 prompt_vPrintf(p, fmt, ap); 365 va_end(ap); 366 } 367} 368 369void 370prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap) 371{ 372 if (p && p->active) { 373 char nfmt[LINE_LEN]; 374 const char *pfmt; 375 376 if (p->TermMode) { 377 /* Stuff '\r' in front of '\n' 'cos we're in raw mode */ 378 int len = strlen(fmt); 379 380 if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n') { 381 strcpy(nfmt, fmt); 382 strcpy(nfmt + len - 1, "\r\n"); 383 pfmt = nfmt; 384 } else 385 pfmt = fmt; 386 } else 387 pfmt = fmt; 388 vfprintf(p->Term, pfmt, ap); 389 fflush(p->Term); 390 p->nonewline = 1; 391 } 392} 393 394void 395prompt_TtyInit(struct prompt *p) 396{ 397 int stat, fd = p ? p->fd_in : STDIN_FILENO; 398 struct termios newtio; 399 400 stat = fcntl(fd, F_GETFL, 0); 401 if (stat > 0) { 402 stat |= O_NONBLOCK; 403 fcntl(fd, F_SETFL, stat); 404 } 405 406 if (p) 407 newtio = p->oldtio; 408 else 409 tcgetattr(fd, &newtio); 410 411 newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 412 newtio.c_iflag = 0; 413 newtio.c_oflag &= ~OPOST; 414 if (!p) 415 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 416 newtio.c_cc[VMIN] = 1; 417 newtio.c_cc[VTIME] = 0; 418 newtio.c_cflag |= CS8; 419 tcsetattr(fd, TCSANOW, &newtio); 420 if (p) 421 p->comtio = newtio; 422} 423 424/* 425 * Set tty into command mode. We allow canonical input and echo processing. 426 */ 427void 428prompt_TtyCommandMode(struct prompt *p) 429{ 430 struct termios newtio; 431 int stat; 432 433 tcgetattr(p->fd_in, &newtio); 434 newtio.c_lflag |= (ECHO | ISIG | ICANON); 435 newtio.c_iflag = p->oldtio.c_iflag; 436 newtio.c_oflag |= OPOST; 437 tcsetattr(p->fd_in, TCSADRAIN, &newtio); 438 439 stat = fcntl(p->fd_in, F_GETFL, 0); 440 if (stat > 0) { 441 stat |= O_NONBLOCK; 442 fcntl(p->fd_in, F_SETFL, stat); 443 } 444 445 p->TermMode = NULL; 446} 447 448/* 449 * Set tty into terminal mode which is used while we invoke term command. 450 */ 451void 452prompt_TtyTermMode(struct prompt *p, struct datalink *dl) 453{ 454 int stat; 455 456 if (p->Term == stdout) 457 tcsetattr(p->fd_in, TCSADRAIN, &p->comtio); 458 459 stat = fcntl(p->fd_in, F_GETFL, 0); 460 if (stat > 0) { 461 stat &= ~O_NONBLOCK; 462 fcntl(p->fd_in, F_SETFL, stat); 463 } 464 p->TermMode = dl; 465} 466 467void 468prompt_TtyOldMode(struct prompt *p) 469{ 470 int stat; 471 472 stat = fcntl(p->fd_in, F_GETFL, 0); 473 if (stat > 0) { 474 stat &= ~O_NONBLOCK; 475 fcntl(p->fd_in, F_SETFL, stat); 476 } 477 478 if (p->Term == stdout) 479 tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio); 480} 481 482pid_t 483prompt_pgrp(struct prompt *p) 484{ 485 return tcgetpgrp(p->fd_in); 486} 487 488int 489PasswdCommand(struct cmdargs const *arg) 490{ 491 const char *pass; 492 493 if (!arg->prompt) { 494 log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n"); 495 return 0; 496 } 497 498 if (arg->prompt->owner == NULL) { 499 log_Printf(LogWARN, "passwd: Not required\n"); 500 return 0; 501 } 502 503 if (arg->argc == arg->argn) 504 pass = ""; 505 else if (arg->argc > arg->argn+1) 506 return -1; 507 else 508 pass = arg->argv[arg->argn]; 509 510 if (!strcmp(arg->prompt->owner->passwd, pass)) 511 arg->prompt->auth = LOCAL_AUTH; 512 else 513 arg->prompt->auth = LOCAL_NO_AUTH; 514 515 return 0; 516} 517 518static struct pppTimer bgtimer; 519 520static void 521prompt_TimedContinue(void *v) 522{ 523 prompt_Continue((struct prompt *)v); 524} 525 526void 527prompt_Continue(struct prompt *p) 528{ 529 timer_Stop(&bgtimer); 530 if (getpgrp() == prompt_pgrp(p)) { 531 prompt_TtyCommandMode(p); 532 p->nonewline = 1; 533 prompt_Required(p); 534 log_ActivatePrompt(p); 535 } else if (!p->owner) { 536 bgtimer.func = prompt_TimedContinue; 537 bgtimer.name = "prompt bg"; 538 bgtimer.load = SECTICKS; 539 bgtimer.arg = p; 540 timer_Start(&bgtimer); 541 } 542} 543 544void 545prompt_Suspend(struct prompt *p) 546{ 547 if (getpgrp() == prompt_pgrp(p)) { 548 prompt_TtyOldMode(p); 549 log_DeactivatePrompt(p); 550 } 551} 552