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