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