mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-10-31 03:43:33 +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;
 | |
| }
 | |
| 
 | |
| };
 |