xref: /freebsd/usr.sbin/ppp/prompt.c (revision 85b542cf4f3d3d3338529aad0f9f006cdaeef9a8)
185b542cfSBrian Somers /*-
285b542cfSBrian Somers  * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
385b542cfSBrian Somers  * All rights reserved.
485b542cfSBrian Somers  *
585b542cfSBrian Somers  * Redistribution and use in source and binary forms, with or without
685b542cfSBrian Somers  * modification, are permitted provided that the following conditions
785b542cfSBrian Somers  * are met:
885b542cfSBrian Somers  * 1. Redistributions of source code must retain the above copyright
985b542cfSBrian Somers  *    notice, this list of conditions and the following disclaimer.
1085b542cfSBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
1185b542cfSBrian Somers  *    notice, this list of conditions and the following disclaimer in the
1285b542cfSBrian Somers  *    documentation and/or other materials provided with the distribution.
1385b542cfSBrian Somers  *
1485b542cfSBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1585b542cfSBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1685b542cfSBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1785b542cfSBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1885b542cfSBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1985b542cfSBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2085b542cfSBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2185b542cfSBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2285b542cfSBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2385b542cfSBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2485b542cfSBrian Somers  * SUCH DAMAGE.
2585b542cfSBrian Somers  *
2685b542cfSBrian Somers  *	$Id$
2785b542cfSBrian Somers  */
2885b542cfSBrian Somers 
2985b542cfSBrian Somers #include <sys/param.h>
3085b542cfSBrian Somers #include <netinet/in.h>
3185b542cfSBrian Somers 
3285b542cfSBrian Somers #include <stdarg.h>
3385b542cfSBrian Somers #include <stdio.h>
3485b542cfSBrian Somers #include <sys/fcntl.h>
3585b542cfSBrian Somers #include <sys/stat.h>
3685b542cfSBrian Somers #include <termios.h>
3785b542cfSBrian Somers #include <unistd.h>
3885b542cfSBrian Somers 
3985b542cfSBrian Somers #include "defs.h"
4085b542cfSBrian Somers #include "timer.h"
4185b542cfSBrian Somers #include "command.h"
4285b542cfSBrian Somers #include "log.h"
4385b542cfSBrian Somers #include "bundle.h"
4485b542cfSBrian Somers #include "descriptor.h"
4585b542cfSBrian Somers #include "prompt.h"
4685b542cfSBrian Somers #include "fsm.h"
4785b542cfSBrian Somers #include "lcp.h"
4885b542cfSBrian Somers #include "auth.h"
4985b542cfSBrian Somers #include "loadalias.h"
5085b542cfSBrian Somers #include "vars.h"
5185b542cfSBrian Somers #include "main.h"
5285b542cfSBrian Somers #include "iplist.h"
5385b542cfSBrian Somers #include "throughput.h"
5485b542cfSBrian Somers #include "ipcp.h"
5585b542cfSBrian Somers 
5685b542cfSBrian Somers static int prompt_nonewline = 1;
5785b542cfSBrian Somers 
5885b542cfSBrian Somers static int
5985b542cfSBrian Somers prompt_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
6085b542cfSBrian Somers {
6185b542cfSBrian Somers   struct prompt *p = descriptor2prompt(d);
6285b542cfSBrian Somers 
6385b542cfSBrian Somers   LogPrintf(LogDEBUG, "descriptor2prompt; %p -> %p\n", d, p);
6485b542cfSBrian Somers 
6585b542cfSBrian Somers   if (p->fd_in >= 0) {
6685b542cfSBrian Somers     if (*n < p->fd_in + 1)
6785b542cfSBrian Somers       *n = p->fd_in + 1;
6885b542cfSBrian Somers     FD_SET(p->fd_in, r);
6985b542cfSBrian Somers     FD_SET(p->fd_in, e);
7085b542cfSBrian Somers     return 2;
7185b542cfSBrian Somers   }
7285b542cfSBrian Somers 
7385b542cfSBrian Somers   return 0;
7485b542cfSBrian Somers }
7585b542cfSBrian Somers 
7685b542cfSBrian Somers static int
7785b542cfSBrian Somers prompt_IsSet(struct descriptor *d, fd_set *fdset)
7885b542cfSBrian Somers {
7985b542cfSBrian Somers   struct prompt *p = descriptor2prompt(d);
8085b542cfSBrian Somers   LogPrintf(LogDEBUG, "descriptor2prompt; %p -> %p\n", d, p);
8185b542cfSBrian Somers   return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset);
8285b542cfSBrian Somers }
8385b542cfSBrian Somers 
8485b542cfSBrian Somers 
8585b542cfSBrian Somers static void
8685b542cfSBrian Somers prompt_ShowHelp(struct prompt *p)
8785b542cfSBrian Somers {
8885b542cfSBrian Somers   prompt_Printf(p, "The following commands are available:\r\n");
8985b542cfSBrian Somers   prompt_Printf(p, " ~p\tEnter Packet mode\r\n");
9085b542cfSBrian Somers   prompt_Printf(p, " ~-\tDecrease log level\r\n");
9185b542cfSBrian Somers   prompt_Printf(p, " ~+\tIncrease log level\r\n");
9285b542cfSBrian Somers   prompt_Printf(p, " ~t\tShow timers (only in \"log debug\" mode)\r\n");
9385b542cfSBrian Somers   prompt_Printf(p, " ~m\tShow memory map (only in \"log debug\" mode)\r\n");
9485b542cfSBrian Somers   prompt_Printf(p, " ~.\tTerminate program\r\n");
9585b542cfSBrian Somers   prompt_Printf(p, " ~?\tThis help\r\n");
9685b542cfSBrian Somers }
9785b542cfSBrian Somers 
9885b542cfSBrian Somers static void
9985b542cfSBrian Somers prompt_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
10085b542cfSBrian Somers {
10185b542cfSBrian Somers   struct prompt *p = descriptor2prompt(d);
10285b542cfSBrian Somers   int n;
10385b542cfSBrian Somers   char ch;
10485b542cfSBrian Somers   static int ttystate;
10585b542cfSBrian Somers   char linebuff[LINE_LEN];
10685b542cfSBrian Somers 
10785b542cfSBrian Somers   LogPrintf(LogDEBUG, "descriptor2prompt; %p -> %p\n", d, p);
10885b542cfSBrian Somers   LogPrintf(LogDEBUG, "termode = %d, p->fd_in = %d, mode = %d\n",
10985b542cfSBrian Somers 	    p->TermMode, p->fd_in, mode);
11085b542cfSBrian Somers 
11185b542cfSBrian Somers   if (!p->TermMode) {
11285b542cfSBrian Somers     n = read(p->fd_in, linebuff, sizeof linebuff - 1);
11385b542cfSBrian Somers     if (n > 0) {
11485b542cfSBrian Somers       if (linebuff[n-1] == '\n')
11585b542cfSBrian Somers         linebuff[--n] = '\0';
11685b542cfSBrian Somers       else
11785b542cfSBrian Somers         linebuff[n] = '\0';
11885b542cfSBrian Somers       if (n)
11985b542cfSBrian Somers         DecodeCommand(bundle, linebuff, n, IsInteractive(0) ? NULL : "Client");
12085b542cfSBrian Somers       prompt_nonewline = 1;
12185b542cfSBrian Somers       prompt_Display(&prompt, bundle);
12285b542cfSBrian Somers     } else if (n <= 0) {
12385b542cfSBrian Somers       LogPrintf(LogPHASE, "Client connection closed.\n");
12485b542cfSBrian Somers       prompt_Drop(&prompt, 0);
12585b542cfSBrian Somers     }
12685b542cfSBrian Somers     return;
12785b542cfSBrian Somers   }
12885b542cfSBrian Somers 
12985b542cfSBrian Somers   /*
13085b542cfSBrian Somers    * We are in terminal mode, decode special sequences
13185b542cfSBrian Somers    */
13285b542cfSBrian Somers   n = read(p->fd_in, &ch, 1);
13385b542cfSBrian Somers   LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
13485b542cfSBrian Somers 
13585b542cfSBrian Somers   if (n > 0) {
13685b542cfSBrian Somers     switch (ttystate) {
13785b542cfSBrian Somers     case 0:
13885b542cfSBrian Somers       if (ch == '~')
13985b542cfSBrian Somers 	ttystate++;
14085b542cfSBrian Somers       else
14185b542cfSBrian Somers 	/* XXX missing return value check */
14285b542cfSBrian Somers 	Physical_Write(bundle->physical, &ch, n);
14385b542cfSBrian Somers       break;
14485b542cfSBrian Somers     case 1:
14585b542cfSBrian Somers       switch (ch) {
14685b542cfSBrian Somers       case '?':
14785b542cfSBrian Somers 	prompt_ShowHelp(p);
14885b542cfSBrian Somers 	break;
14985b542cfSBrian Somers       case 'p':
15085b542cfSBrian Somers 
15185b542cfSBrian Somers 	/*
15285b542cfSBrian Somers 	 * XXX: Should check carrier.
15385b542cfSBrian Somers 	 */
15485b542cfSBrian Somers 	if (LcpInfo.fsm.state <= ST_CLOSED)
15585b542cfSBrian Somers 	  PacketMode(bundle, 0);
15685b542cfSBrian Somers 	break;
15785b542cfSBrian Somers       case '.':
15885b542cfSBrian Somers 	p->TermMode = 1;
15985b542cfSBrian Somers 	prompt_TtyCommandMode(&prompt);
16085b542cfSBrian Somers         prompt_nonewline = 0;
16185b542cfSBrian Somers         prompt_Display(&prompt, bundle);
16285b542cfSBrian Somers 	break;
16385b542cfSBrian Somers       case 't':
16485b542cfSBrian Somers 	if (LogIsKept(LogDEBUG)) {
16585b542cfSBrian Somers 	  ShowTimers();
16685b542cfSBrian Somers 	  break;
16785b542cfSBrian Somers 	}
16885b542cfSBrian Somers       case 'm':
16985b542cfSBrian Somers 	if (LogIsKept(LogDEBUG)) {
17085b542cfSBrian Somers 	  ShowMemMap(NULL);
17185b542cfSBrian Somers 	  break;
17285b542cfSBrian Somers 	}
17385b542cfSBrian Somers       default:
17485b542cfSBrian Somers 	if (Physical_Write(bundle->physical, &ch, n) < 0)
17585b542cfSBrian Somers 	  LogPrintf(LogERROR, "error writing to modem.\n");
17685b542cfSBrian Somers 	break;
17785b542cfSBrian Somers       }
17885b542cfSBrian Somers       ttystate = 0;
17985b542cfSBrian Somers       break;
18085b542cfSBrian Somers     }
18185b542cfSBrian Somers   }
18285b542cfSBrian Somers }
18385b542cfSBrian Somers 
18485b542cfSBrian Somers static void
18585b542cfSBrian Somers prompt_Write(struct descriptor *d, const fd_set *fdset)
18685b542cfSBrian Somers {
18785b542cfSBrian Somers   /* We don't set the write descriptor (yet) */
18885b542cfSBrian Somers }
18985b542cfSBrian Somers 
19085b542cfSBrian Somers struct prompt prompt = {
19185b542cfSBrian Somers   {
19285b542cfSBrian Somers     PROMPT_DESCRIPTOR,
19385b542cfSBrian Somers     NULL,
19485b542cfSBrian Somers     prompt_UpdateSet,
19585b542cfSBrian Somers     prompt_IsSet,
19685b542cfSBrian Somers     prompt_Read,
19785b542cfSBrian Somers     prompt_Write
19885b542cfSBrian Somers   },
19985b542cfSBrian Somers   -1,
20085b542cfSBrian Somers   -1,
20185b542cfSBrian Somers   NULL
20285b542cfSBrian Somers };
20385b542cfSBrian Somers 
20485b542cfSBrian Somers int
20585b542cfSBrian Somers prompt_Init(struct prompt *p, int fd)
20685b542cfSBrian Somers {
20785b542cfSBrian Somers   if (p->Term && p->Term != stdout)
20885b542cfSBrian Somers     return 0;                       /* must prompt_Drop() first */
20985b542cfSBrian Somers 
21085b542cfSBrian Somers   if (fd == PROMPT_NONE) {
21185b542cfSBrian Somers     p->fd_in = p->fd_out = -1;
21285b542cfSBrian Somers     p->Term = NULL;
21385b542cfSBrian Somers   } else if (fd == PROMPT_STD) {
21485b542cfSBrian Somers     p->fd_in = STDIN_FILENO;
21585b542cfSBrian Somers     p->fd_out = STDOUT_FILENO;
21685b542cfSBrian Somers     p->Term = stdout;
21785b542cfSBrian Somers   } else {
21885b542cfSBrian Somers     p->fd_in = p->fd_out = fd;
21985b542cfSBrian Somers     p->Term = fdopen(fd, "a+");
22085b542cfSBrian Somers   }
22185b542cfSBrian Somers   p->TermMode = 0;
22285b542cfSBrian Somers   tcgetattr(STDIN_FILENO, &p->oldtio);	/* Save original tty mode */
22385b542cfSBrian Somers 
22485b542cfSBrian Somers   return 1;
22585b542cfSBrian Somers }
22685b542cfSBrian Somers 
22785b542cfSBrian Somers int
22885b542cfSBrian Somers prompt_Display(struct prompt *p, struct bundle *bundle)
22985b542cfSBrian Somers {
23085b542cfSBrian Somers   const char *pconnect, *pauth;
23185b542cfSBrian Somers 
23285b542cfSBrian Somers   if (!p->Term || p->TermMode || CleaningUp)
23385b542cfSBrian Somers     return;
23485b542cfSBrian Somers 
23585b542cfSBrian Somers   if (prompt_nonewline)
23685b542cfSBrian Somers     prompt_nonewline = 0;
23785b542cfSBrian Somers   else
23885b542cfSBrian Somers     fprintf(p->Term, "\n");
23985b542cfSBrian Somers 
24085b542cfSBrian Somers   if (VarLocalAuth == LOCAL_AUTH)
24185b542cfSBrian Somers     pauth = " ON ";
24285b542cfSBrian Somers   else
24385b542cfSBrian Somers     pauth = " on ";
24485b542cfSBrian Somers 
24585b542cfSBrian Somers   if (IpcpInfo.fsm.state == ST_OPENED)
24685b542cfSBrian Somers     pconnect = "PPP";
24785b542cfSBrian Somers   else if (bundle_Phase(bundle) == PHASE_NETWORK)
24885b542cfSBrian Somers     pconnect = "PPp";
24985b542cfSBrian Somers   else if (bundle_Phase(bundle) == PHASE_AUTHENTICATE)
25085b542cfSBrian Somers     pconnect = "Ppp";
25185b542cfSBrian Somers   else
25285b542cfSBrian Somers     pconnect = "ppp";
25385b542cfSBrian Somers 
25485b542cfSBrian Somers   fprintf(p->Term, "%s%s%s> ", pconnect, pauth, VarShortHost);
25585b542cfSBrian Somers   fflush(p->Term);
25685b542cfSBrian Somers }
25785b542cfSBrian Somers 
25885b542cfSBrian Somers void
25985b542cfSBrian Somers prompt_Drop(struct prompt *p, int verbose)
26085b542cfSBrian Somers {
26185b542cfSBrian Somers   if (p->Term && p->Term != stdout) {
26285b542cfSBrian Somers     FILE *oVarTerm;
26385b542cfSBrian Somers 
26485b542cfSBrian Somers     oVarTerm = p->Term;
26585b542cfSBrian Somers     p->Term = NULL;
26685b542cfSBrian Somers     if (oVarTerm)
26785b542cfSBrian Somers       fclose(oVarTerm);
26885b542cfSBrian Somers     close(p->fd_in);
26985b542cfSBrian Somers     if (p->fd_out != p->fd_in)
27085b542cfSBrian Somers       close(p->fd_out);
27185b542cfSBrian Somers     p->fd_in = p->fd_out = -1;
27285b542cfSBrian Somers     if (verbose)
27385b542cfSBrian Somers       LogPrintf(LogPHASE, "Client connection dropped.\n");
27485b542cfSBrian Somers   }
27585b542cfSBrian Somers }
27685b542cfSBrian Somers 
27785b542cfSBrian Somers void
27885b542cfSBrian Somers prompt_Printf(struct prompt *p, const char *fmt,...)
27985b542cfSBrian Somers {
28085b542cfSBrian Somers   if (p->Term) {
28185b542cfSBrian Somers     va_list ap;
28285b542cfSBrian Somers 
28385b542cfSBrian Somers     va_start(ap, fmt);
28485b542cfSBrian Somers     vfprintf(p->Term, fmt, ap);
28585b542cfSBrian Somers     fflush(p->Term);
28685b542cfSBrian Somers     va_end(ap);
28785b542cfSBrian Somers   }
28885b542cfSBrian Somers }
28985b542cfSBrian Somers 
29085b542cfSBrian Somers void
29185b542cfSBrian Somers prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap)
29285b542cfSBrian Somers {
29385b542cfSBrian Somers   if (p->Term) {
29485b542cfSBrian Somers     vfprintf(p->Term, fmt, ap);
29585b542cfSBrian Somers     fflush(p->Term);
29685b542cfSBrian Somers   }
29785b542cfSBrian Somers }
29885b542cfSBrian Somers 
29985b542cfSBrian Somers void
30085b542cfSBrian Somers prompt_TtyInit(struct prompt *p, int DontWantInt)
30185b542cfSBrian Somers {
30285b542cfSBrian Somers   struct termios newtio;
30385b542cfSBrian Somers   int stat;
30485b542cfSBrian Somers 
30585b542cfSBrian Somers   stat = fcntl(p->fd_in, F_GETFL, 0);
30685b542cfSBrian Somers   if (stat > 0) {
30785b542cfSBrian Somers     stat |= O_NONBLOCK;
30885b542cfSBrian Somers     (void) fcntl(p->fd_in, F_SETFL, stat);
30985b542cfSBrian Somers   }
31085b542cfSBrian Somers   newtio = p->oldtio;
31185b542cfSBrian Somers   newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
31285b542cfSBrian Somers   newtio.c_iflag = 0;
31385b542cfSBrian Somers   newtio.c_oflag &= ~OPOST;
31485b542cfSBrian Somers   newtio.c_cc[VEOF] = _POSIX_VDISABLE;
31585b542cfSBrian Somers   if (DontWantInt)
31685b542cfSBrian Somers     newtio.c_cc[VINTR] = _POSIX_VDISABLE;
31785b542cfSBrian Somers   newtio.c_cc[VMIN] = 1;
31885b542cfSBrian Somers   newtio.c_cc[VTIME] = 0;
31985b542cfSBrian Somers   newtio.c_cflag |= CS8;
32085b542cfSBrian Somers   tcsetattr(p->fd_in, TCSANOW, &newtio);
32185b542cfSBrian Somers   p->comtio = newtio;
32285b542cfSBrian Somers }
32385b542cfSBrian Somers 
32485b542cfSBrian Somers /*
32585b542cfSBrian Somers  *  Set tty into command mode. We allow canonical input and echo processing.
32685b542cfSBrian Somers  */
32785b542cfSBrian Somers void
32885b542cfSBrian Somers prompt_TtyCommandMode(struct prompt *p)
32985b542cfSBrian Somers {
33085b542cfSBrian Somers   struct termios newtio;
33185b542cfSBrian Somers   int stat;
33285b542cfSBrian Somers 
33385b542cfSBrian Somers   if (!(mode & MODE_INTER))
33485b542cfSBrian Somers     return;
33585b542cfSBrian Somers 
33685b542cfSBrian Somers   tcgetattr(p->fd_in, &newtio);
33785b542cfSBrian Somers   newtio.c_lflag |= (ECHO | ISIG | ICANON);
33885b542cfSBrian Somers   newtio.c_iflag = p->oldtio.c_iflag;
33985b542cfSBrian Somers   newtio.c_oflag |= OPOST;
34085b542cfSBrian Somers   tcsetattr(p->fd_in, TCSADRAIN, &newtio);
34185b542cfSBrian Somers   stat = fcntl(p->fd_in, F_GETFL, 0);
34285b542cfSBrian Somers   if (stat > 0) {
34385b542cfSBrian Somers     stat |= O_NONBLOCK;
34485b542cfSBrian Somers     (void) fcntl(p->fd_in, F_SETFL, stat);
34585b542cfSBrian Somers   }
34685b542cfSBrian Somers   p->TermMode = 0;
34785b542cfSBrian Somers }
34885b542cfSBrian Somers 
34985b542cfSBrian Somers /*
35085b542cfSBrian Somers  * Set tty into terminal mode which is used while we invoke term command.
35185b542cfSBrian Somers  */
35285b542cfSBrian Somers void
35385b542cfSBrian Somers prompt_TtyTermMode(struct prompt *p)
35485b542cfSBrian Somers {
35585b542cfSBrian Somers   int stat;
35685b542cfSBrian Somers 
35785b542cfSBrian Somers   tcsetattr(p->fd_in, TCSADRAIN, &p->comtio);
35885b542cfSBrian Somers   stat = fcntl(p->fd_in, F_GETFL, 0);
35985b542cfSBrian Somers   if (stat > 0) {
36085b542cfSBrian Somers     stat &= ~O_NONBLOCK;
36185b542cfSBrian Somers     (void) fcntl(p->fd_in, F_SETFL, stat);
36285b542cfSBrian Somers   }
36385b542cfSBrian Somers   p->TermMode = 1;
36485b542cfSBrian Somers }
36585b542cfSBrian Somers 
36685b542cfSBrian Somers void
36785b542cfSBrian Somers prompt_TtyOldMode(struct prompt *p)
36885b542cfSBrian Somers {
36985b542cfSBrian Somers   int stat;
37085b542cfSBrian Somers 
37185b542cfSBrian Somers   stat = fcntl(p->fd_in, F_GETFL, 0);
37285b542cfSBrian Somers   if (stat > 0) {
37385b542cfSBrian Somers     stat &= ~O_NONBLOCK;
37485b542cfSBrian Somers     (void) fcntl(p->fd_in, F_SETFL, stat);
37585b542cfSBrian Somers   }
37685b542cfSBrian Somers   tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio);
37785b542cfSBrian Somers }
37885b542cfSBrian Somers 
37985b542cfSBrian Somers pid_t
38085b542cfSBrian Somers prompt_pgrp(struct prompt *p)
38185b542cfSBrian Somers {
38285b542cfSBrian Somers   return p->Term ? tcgetpgrp(p->fd_in) : -1;
38385b542cfSBrian Somers }
384