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