mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-04 05:43:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			245 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* Copyright 2009, 2010 Free Software Foundation, Inc.
 | 
						|
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
						|
* Copyright 2011, 2012, 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 distribuion.
 | 
						|
*
 | 
						|
* 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 <stdlib.h>
 | 
						|
#include <string>
 | 
						|
#include <map>
 | 
						|
#include <iostream>
 | 
						|
 | 
						|
#include <Globals.h>
 | 
						|
#include <Logger.h>
 | 
						|
#include "CLI.h"
 | 
						|
 | 
						|
namespace CommandLine {
 | 
						|
 | 
						|
using std::map; using std::string; using std::ostream;
 | 
						|
 | 
						|
/** Standard responses in the CLI, much mach erorrCode enum. */
 | 
						|
static const char* standardResponses[] = {
 | 
						|
	"success", // 0
 | 
						|
	"wrong number of arguments", // 1
 | 
						|
	"bad argument(s)", // 2
 | 
						|
	"command not found", // 3
 | 
						|
	"too many arguments for parser", // 4
 | 
						|
	"command failed", // 5
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
 | 
						|
CLIStatus Parser::execute(char* line, ostream& os) const
 | 
						|
{
 | 
						|
	LOG(INFO) << "executing console command: " << line;
 | 
						|
 | 
						|
	// tokenize
 | 
						|
	char *argv[mMaxArgs];
 | 
						|
	int argc = 0;
 | 
						|
	// This is (almost) straight from the man page for strsep.
 | 
						|
	while (line && argc < mMaxArgs) {
 | 
						|
		while (*line == ' ') { line++; }
 | 
						|
		if (! *line) { break; }
 | 
						|
		char *anarg = line;
 | 
						|
		if (*line == '"') {		// We allow a quoted string as a single argument.  Quotes themselves are removed.
 | 
						|
			line++; anarg++;
 | 
						|
			char *endquote = strchr(line,'"');
 | 
						|
			if (endquote == NULL) {
 | 
						|
				os << "error: Missing quote."<<endl;
 | 
						|
				return FAILURE;
 | 
						|
			}
 | 
						|
			if (!(endquote[1] == 0 || endquote[1] == ' ')) {
 | 
						|
				os << "error: Embedded quotes not allowed." << endl;
 | 
						|
				return FAILURE;
 | 
						|
			}
 | 
						|
			*endquote = 0;
 | 
						|
			line = endquote+1;
 | 
						|
		} else if (strsep(&line," ") == NULL) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		argv[argc++] = anarg;
 | 
						|
	}
 | 
						|
	//for (ap=argv; (*ap=strsep(&line," ")) != NULL; ) {
 | 
						|
	//	if (**ap != '\0') {
 | 
						|
	//		if (++ap >= &argv[mMaxArgs]) break;
 | 
						|
	//		else argc++;
 | 
						|
	//	}
 | 
						|
	//}
 | 
						|
	// Blank line?
 | 
						|
	if (!argc) return SUCCESS;
 | 
						|
	// Find the command.
 | 
						|
	//printf("args=%d\n",argc);
 | 
						|
	//for (int i = 0; i < argc; i++) { printf("argv[%d]=%s\n",i,argv[i]); }
 | 
						|
	ParseTable::const_iterator cfp = mParseTable.find(argv[0]);
 | 
						|
	if (cfp == mParseTable.end()) {
 | 
						|
		return NOT_FOUND;
 | 
						|
	}
 | 
						|
	CLICommand func;
 | 
						|
	func = cfp->second;
 | 
						|
	// Do it.
 | 
						|
	CLIStatus retVal;
 | 
						|
	try {
 | 
						|
		retVal = (*func)(argc,argv,os);
 | 
						|
	} catch (CLIParseError &pe) {
 | 
						|
		os << pe.msg << endl;
 | 
						|
		retVal = SUCCESS;	// Dont print any further messages.
 | 
						|
	}
 | 
						|
	// Give hint on bad # args.
 | 
						|
	if (retVal==BAD_NUM_ARGS) os << help(argv[0]) << endl;
 | 
						|
	return retVal;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// This is called from a runloop in apps/OpenBTS.cpp
 | 
						|
// If it returns a negative number OpenBTS exists.
 | 
						|
CLIStatus Parser::process(const char* line, ostream& os) const
 | 
						|
{
 | 
						|
	static Mutex oneCommandAtATime;
 | 
						|
	ScopedLock lock(oneCommandAtATime);
 | 
						|
	char *newLine = strdup(line);
 | 
						|
	LOG(INFO) << "CLI executing command:" <<(line?line:"null");
 | 
						|
	CLIStatus retVal = execute(newLine,os);
 | 
						|
	free(newLine);
 | 
						|
	if (retVal < CLI_EXIT || retVal > FAILURE) {
 | 
						|
		os << "Unrecognized CLI command exit status: "<<retVal << endl;
 | 
						|
	} else if (retVal != SUCCESS) {
 | 
						|
		os << standardResponses[retVal] << endl;
 | 
						|
	}
 | 
						|
	return retVal;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// (pat) This is no longer used - see CLIServer.
 | 
						|
static void *commandLineFunc(Parser *parser)
 | 
						|
{
 | 
						|
	const char *prompt = "OpenBTS> ";		//gConfig.getStr("CLI.Prompt");  CLI.Prompt no longer defined.
 | 
						|
	try {
 | 
						|
#ifdef HAVE_READLINE
 | 
						|
		using_history();
 | 
						|
		while (true) {
 | 
						|
			clearerr(stdin);	// Control-D may set the eof bit which causes getline to return immediately.  Fix it.
 | 
						|
			char *inbuf = readline(prompt);
 | 
						|
			if (inbuf) {
 | 
						|
				if (*inbuf) {
 | 
						|
					add_history(inbuf);
 | 
						|
					// The parser returns -1 on exit.
 | 
						|
					if (parser->process(inbuf, cout, cin)<0) {
 | 
						|
						free(inbuf);
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				free(inbuf);
 | 
						|
			} else {
 | 
						|
				printf("EOF ignored\n");
 | 
						|
			}
 | 
						|
			sleep(1); // in case something goofs up here, dont steal all the cpu cycles.
 | 
						|
		}
 | 
						|
#else
 | 
						|
		while (true) {
 | 
						|
			//cout << endl << 
 | 
						|
			cout << endl << prompt;
 | 
						|
			cout.flush();
 | 
						|
			char inbuf[1024];
 | 
						|
			cin.clear();	// Control-D may set the eof bit which causes getline to return immediately.  Fix it.
 | 
						|
			cin.getline(inbuf,1024,'\n');		// istream::getline
 | 
						|
			// The parser returns -1 on exit.
 | 
						|
			if (parser->process(inbuf,cout)<0) break;
 | 
						|
			sleep(1); // in case something goofs up here, dont steal all the cpu cycles.
 | 
						|
		}
 | 
						|
#endif
 | 
						|
	} catch (ConfigurationTableKeyNotFound e) {
 | 
						|
		LOG(EMERG) << "required configuration parameter " << e.key() << " not defined, aborting";
 | 
						|
		gReports.incr("OpenBTS.Exit.Error.ConfigurationParamterNotFound");
 | 
						|
	}
 | 
						|
 | 
						|
	exit(0);	// Exit OpenBTS
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void Parser::startCommandLine()	// (pat) Start a simple command line processor as a separate thread.
 | 
						|
{
 | 
						|
	static Thread commandLineThread;
 | 
						|
	commandLineThread.start( (void*(*)(void*)) commandLineFunc,this);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const char * Parser::help(const string& cmd) const
 | 
						|
{
 | 
						|
	HelpTable::const_iterator hp = mHelpTable.find(cmd);
 | 
						|
	if (hp==mHelpTable.end()) return "no help available";
 | 
						|
	return hp->second.c_str();
 | 
						|
}
 | 
						|
 | 
						|
void Parser::printHelp(ostream &os) const
 | 
						|
{
 | 
						|
	ParseTable::const_iterator cp = this->begin();
 | 
						|
	os << endl << "Type \"help\" followed by the command name for help on that command." << endl << endl;
 | 
						|
	int c=0;
 | 
						|
	const int cols = 3;
 | 
						|
	while (cp != this->end()) {
 | 
						|
		const string& wd = cp->first;
 | 
						|
		os << wd << '\t';
 | 
						|
		if (wd.size()<8) os << '\t';
 | 
						|
		++cp;
 | 
						|
		c++;
 | 
						|
		if (c%cols==0) os << endl;
 | 
						|
	}
 | 
						|
	if (c%cols!=0) os << endl;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// Parse options in optstring out of argc,argv.
 | 
						|
// The optstring is a space separated list of options.  The options need not start with '-'.  To recognize just "-" or "--" just add it in.
 | 
						|
// Return a map containing the options found; if option in optstring was followed by ':', map value will be the next argv argument, otherwise "true".
 | 
						|
// Leave argc,argv pointing at the first argument after the options, ie, on return argc is the number of non-option arguments remaining in argv.
 | 
						|
// This routine does not allow combining options, ie, -a -b != -ab
 | 
						|
map<string,string> cliParse(int &argc, char **&argv, ostream &os, const char *optstring)
 | 
						|
{
 | 
						|
	map<string,string> options;		// The result
 | 
						|
	// Skip the command name.
 | 
						|
	argc--, argv++;
 | 
						|
	// Parse args.
 | 
						|
	for ( ; argc > 0; argc--, argv++ ) {
 | 
						|
		char *arg = argv[0];
 | 
						|
		// The argv to match may not contain ':' to prevent the pathological case, for example, where optionlist contains "-a:" and command line arg is "-a:"
 | 
						|
		if (strchr(arg,':')) { return options; }	// Can't parse this, too dangerous.
 | 
						|
		const char *op = strstr(optstring,arg);
 | 
						|
		if (op && (op == optstring || op[-1] == ' ')) {
 | 
						|
			const char *ep = op + strlen(arg);
 | 
						|
			if (*ep == ':') {
 | 
						|
				// This valid option requires an argument.
 | 
						|
				argc--, argv++;
 | 
						|
				if (argc <= 0) { throw CLIParseError(format("expected argument after: %s",arg)); }
 | 
						|
				options[arg] = string(argv[0]);
 | 
						|
				continue;
 | 
						|
			} else if (*ep == 0 || *ep == ' ') {
 | 
						|
				// This valid option does not require an argument.
 | 
						|
				options[arg] = string("true");
 | 
						|
				continue;
 | 
						|
			} else {
 | 
						|
				// Partial match of something in optstring; drop through to treat it like any other argument.
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			break;	// Return when we find the first non-option.
 | 
						|
		}
 | 
						|
		// An argument beginning with - and not in optstring is an unrecognized option and is an error.
 | 
						|
		if (*arg == '-') { throw CLIParseError(format("unrecognized argument: %s",arg)); }
 | 
						|
		return options;
 | 
						|
	}
 | 
						|
	return options;
 | 
						|
}
 | 
						|
 | 
						|
};
 |