mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 21:33:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			256 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* Copyright 2014 Range Networks, Inc.
 | 
						|
*
 | 
						|
* This software is distributed under multiple licenses;
 | 
						|
* see the COPYING file in the main directory for licensing
 | 
						|
* information for this specific distribution.
 | 
						|
*
 | 
						|
* This use of this software may be subject to additional restrictions.
 | 
						|
* See the LEGAL file in the main directory for details.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include "CLI.h"
 | 
						|
#include <Globals.h>
 | 
						|
#include <Configuration.h>
 | 
						|
 | 
						|
using namespace CommandLine;
 | 
						|
 | 
						|
void Parser::Prompt() const
 | 
						|
{
 | 
						|
    printf("%s> ",this->mCommandName.c_str()); fflush(stdout);
 | 
						|
}
 | 
						|
 | 
						|
int Parser::Execute(bool console, const char cmdbuf[], int outfd)
 | 
						|
{
 | 
						|
    // step 2 - execute
 | 
						|
    gReports.incr("OpenBTS.CLI.Command");
 | 
						|
    const char *type = console ? "Console: " : "Socket: ";
 | 
						|
    LOG(INFO) << type << "received command \"" << cmdbuf << "\"";
 | 
						|
    std::ostringstream sout;
 | 
						|
    int res = this->process(cmdbuf,sout);
 | 
						|
    const std::string rspString= sout.str();
 | 
						|
    const char* rsp = rspString.c_str();
 | 
						|
 | 
						|
    // step 3 - respond, put length at start of response
 | 
						|
    LOG(INFO) << type << "sending " << strlen(rsp) << "-char result";
 | 
						|
    int len = strlen(rsp);
 | 
						|
    if (console)
 | 
						|
    {
 | 
						|
        if (write(outfd,rsp,len) != len) {
 | 
						|
            LOG(ERR) << type << "can't send CLI response";
 | 
						|
            gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
        }
 | 
						|
        this->Prompt();
 | 
						|
    } else
 | 
						|
    {
 | 
						|
        len = htonl(len);
 | 
						|
        int sendLen = sizeof(len);
 | 
						|
        int sentLen = 0;
 | 
						|
        if ((sentLen = send(outfd, &len, sendLen, 0)) != sendLen)
 | 
						|
        {
 | 
						|
            LOG(ERR) << type << "can't send CLI response";
 | 
						|
            gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
        } else
 | 
						|
        {
 | 
						|
            sendLen = strlen(rsp);
 | 
						|
            if ((sentLen = send(outfd,rsp,sendLen,0)) != sendLen) {
 | 
						|
                LOG(ERR) << type << "can't send CLI response";
 | 
						|
                gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// (pat) 4-2014: Moved this code written by Dave G. from OpenBTS.cpp to this directory so that it can be shared by multiple apps.
 | 
						|
// I did not bother to rename all the gReports variables.
 | 
						|
void Parser::cliServer()
 | 
						|
{
 | 
						|
	assert(mCommandName.size());	// Dont leave this empty.
 | 
						|
 | 
						|
	int netSockFd = -1;
 | 
						|
 | 
						|
	// CLI Interface code
 | 
						|
	string CLIPortOpt = "CLI.Port", CLIInterfaceOpt = "CLI.Interface";
 | 
						|
	if (mCommandName != "OpenBTS") {
 | 
						|
		CLIPortOpt = mCommandName + "." + CLIPortOpt;
 | 
						|
		CLIInterfaceOpt = mCommandName + "." + CLIInterfaceOpt;
 | 
						|
	}
 | 
						|
	string netPort = gConfig.getStr(CLIPortOpt);
 | 
						|
	string neInterface = gConfig.defines(CLIInterfaceOpt) ? gConfig.getStr(CLIInterfaceOpt) : string(""); // TODO: Future implementation
 | 
						|
	bool netTcp = false;
 | 
						|
 | 
						|
	if (netPort.size())
 | 
						|
	{
 | 
						|
		in_addr_t iInterface = neInterface.size() == 0 ? inet_addr("127.0.0.1") : inet_addr(neInterface.c_str());
 | 
						|
		// printf("Interface mask 0x%08X\n", iInterface);
 | 
						|
		struct sockaddr_in servAddr;
 | 
						|
		memset(&servAddr, 0, sizeof(servAddr));
 | 
						|
		servAddr.sin_family = AF_INET;
 | 
						|
		servAddr.sin_addr.s_addr = iInterface;
 | 
						|
		int port = 0;
 | 
						|
		netTcp = true;
 | 
						|
		port = atoi(netPort.c_str());
 | 
						|
		servAddr.sin_port = htons(port);
 | 
						|
 | 
						|
		netSockFd = socket(AF_INET,SOCK_STREAM,0);
 | 
						|
		if (netSockFd<0) {
 | 
						|
			perror("creating CLI network stream socket");
 | 
						|
			LOG(ALERT) << "cannot create network tcp socket for CLI";
 | 
						|
			gReports.incr("OpenBTS.Exit.CLI.Socket");
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
		int optval = 1;
 | 
						|
		setsockopt(netSockFd, SOL_SOCKET,SO_REUSEADDR, &optval, sizeof(optval));
 | 
						|
		if (bind(netSockFd, (struct sockaddr *) &servAddr, sizeof(struct sockaddr_in))) {
 | 
						|
			perror("binding name to network socket");
 | 
						|
			LOG(ALERT) << "cannot bind socket for CLI at " << (netTcp ? "tcp:" : "udp:" ) << netPort;
 | 
						|
			gReports.incr("OpenBTS.Exit.CLI.Socket");
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
		if (netTcp) listen(netSockFd, 10);
 | 
						|
	}
 | 
						|
 | 
						|
	if (netSockFd != -1)
 | 
						|
	{
 | 
						|
		char buf[BUFSIZ];
 | 
						|
		snprintf(buf, sizeof(buf)-1, "OpenBTSCLI network socket support for %s:%s\n", ( netTcp ? "tcp" : "udp") , netPort.c_str());
 | 
						|
		COUT(buf);
 | 
						|
	}
 | 
						|
 | 
						|
	fd_set rdFds, curFds;
 | 
						|
	FD_ZERO(&rdFds);
 | 
						|
	if (netSockFd != -1) FD_SET(netSockFd, &rdFds);
 | 
						|
	if (isatty(0)) // if not running from a terminal, don't bother
 | 
						|
	{
 | 
						|
    FD_SET(0, &rdFds); // console
 | 
						|
    this->Prompt();
 | 
						|
	}
 | 
						|
	char cmdbuf[BUFSIZ];
 | 
						|
	bool isRunning = true;
 | 
						|
	struct timeval tv;
 | 
						|
	while (isRunning) {
 | 
						|
		// First, build mask of possible input channels
 | 
						|
		curFds = rdFds;
 | 
						|
		tv.tv_sec = 60; tv.tv_usec = 0;
 | 
						|
		int selRet = select(FD_SETSIZE, &curFds, NULL, NULL, &tv);
 | 
						|
		if (selRet > 0)
 | 
						|
		{
 | 
						|
			for (int i = 0; i < FD_SETSIZE; i++)
 | 
						|
			{
 | 
						|
				if (!FD_ISSET(i, &curFds))
 | 
						|
					continue;
 | 
						|
                if (i == 0)
 | 
						|
                {
 | 
						|
                    if (NULL == fgets(cmdbuf, sizeof(cmdbuf)-1, stdin)) { continue; }
 | 
						|
                    char *p = strchr(cmdbuf, '\n');
 | 
						|
                    if (p != NULL)
 | 
						|
                        *p = '\0';
 | 
						|
                    else
 | 
						|
                        cmdbuf[BUFSIZ-1] = '\0';
 | 
						|
                    int res = Execute(true, cmdbuf, 1); // and ignore the result
 | 
						|
                    if (res < 0) { isRunning = false; }
 | 
						|
                } else if (i == netSockFd) // accept (tcp)
 | 
						|
				{
 | 
						|
					if (netTcp)
 | 
						|
					{
 | 
						|
						struct sockaddr peer;
 | 
						|
						socklen_t len = sizeof(peer);
 | 
						|
						// int fd = accept(i, NULL, NULL); // don't care who it's from
 | 
						|
						int fd = accept(i, &peer, &len);	// (pat) On the other hand, might be interesting to know who it's from.
 | 
						|
						if (fd < 0) {
 | 
						|
							LOG(ERR) << "can't accept network stream connection";
 | 
						|
							gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
							break;
 | 
						|
						}
 | 
						|
						// (pat) Could make an argument that we should always print this message as a security measure,
 | 
						|
						// since the peer is allowed complete access to OpenBTS.
 | 
						|
						if (1) {
 | 
						|
							// Lets print out who it was from:
 | 
						|
							char addrString[256];
 | 
						|
							struct sockaddr_in *sp = (struct sockaddr_in*)&peer;
 | 
						|
							if (const char *ret = inet_ntop(AF_INET,&(sp->sin_addr),addrString,255)) {
 | 
						|
								LOG(INFO) << format("Accepting CLI connection from %s:%d", ret,(int)ntohs(sp->sin_port));
 | 
						|
							}
 | 
						|
						}
 | 
						|
						FD_SET(fd, &rdFds); // so that we can select on it next time around
 | 
						|
					}
 | 
						|
				} else if (FD_ISSET(i, &curFds)) // a tcp socket that had previously been accepted
 | 
						|
				{
 | 
						|
					// step 1 - read the command.  Format is <len>data, because it may take several reads to get the full read
 | 
						|
					//			along a stream.
 | 
						|
					int len;
 | 
						|
					int nread = recv(i, &len, sizeof(len), 0);
 | 
						|
					if (nread < 0)
 | 
						|
					{
 | 
						|
						LOG(ERR) << "can't read CLI request from stream";
 | 
						|
						gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					if (len == 0) // clsoe
 | 
						|
					{
 | 
						|
						FD_CLR(i, &rdFds);
 | 
						|
						shutdown(i, SHUT_RDWR);
 | 
						|
						close(i);
 | 
						|
						continue; // go to next socket
 | 
						|
					}
 | 
						|
					if (len < (int) sizeof(len)) // should never get here
 | 
						|
					{
 | 
						|
						char buf[BUFSIZ];
 | 
						|
						sprintf(buf, "Unable to read complete length, s.b. %d bytes, got %d bytes\n", sizeof(len), len);
 | 
						|
						LOG(ERR) << buf;
 | 
						|
						gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					len = ntohl(len);
 | 
						|
					int off = 0;
 | 
						|
					while(len != 0)
 | 
						|
					{
 | 
						|
						nread = recv(i, &cmdbuf[off], len, 0);
 | 
						|
						if (nread < 0)
 | 
						|
						{
 | 
						|
							LOG(ERR) << "can't read CLI request from stream";
 | 
						|
							gReports.incr("OpenBTS.CLI.Command.ResponseFailure");
 | 
						|
							break;
 | 
						|
						}
 | 
						|
						if (nread == 0) // close
 | 
						|
						{
 | 
						|
							FD_CLR(i, &rdFds);
 | 
						|
							shutdown(i, SHUT_RDWR);
 | 
						|
							close(i);
 | 
						|
							continue; // go to next socket
 | 
						|
						}
 | 
						|
						off += nread;
 | 
						|
						len -= nread;
 | 
						|
					}
 | 
						|
					cmdbuf[off] = '\0';
 | 
						|
 | 
						|
                    int res = Execute(false, cmdbuf, i);
 | 
						|
                    // (comment by Dave G) NOTE: This change was made because we need to exit as
 | 
						|
                    // quickly as possible, or we end up getting some other
 | 
						|
                    // threads faulting as objects vanish from under them.
 | 
						|
					// (pat 3-2014) I dont think the above helped.  But I had to fix up the exit routines in order to run gprof,
 | 
						|
					// so it is ok to exit normally now.
 | 
						|
                    if (res < 0) {
 | 
						|
						isRunning = false;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else if (selRet < 0) {
 | 
						|
			// (pat) TEMPORARY!  Watch this to try to figure out where the unexpected signal is coming from.
 | 
						|
			perror("system call during CLI select loop");
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (netSockFd != -1) close(netSockFd);	// Why bother?
 | 
						|
}
 |