2014-10-16 14:42:05 -07:00
/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex , proprietary hardware systems .
*
* 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 the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information .
*
* This use of this software may be subject to additional restrictions .
* See the LEGAL file in the main directory for details .
*/
# include <iostream>
# include <iomanip>
# include <fstream>
# include <iterator>
# include <string.h>
# include <stdlib.h>
# include <math.h>
# include <time.h>
# include <algorithm> // for sort()
# include <config.h>
# include <Logger.h>
# include <UMTSConfig.h>
# include <TransactionTable.h>
# include <UMTSLogicalChannel.h>
# include <MemoryLeak.h>
# include "CLI.h"
# undef WARNING
using namespace std ;
using namespace CommandLine ;
// (mike) Just following the example of SGSN because of linker errors but why do we do this?
namespace UMTS {
extern CommandLine : : CLIStatus rrcTest ( int argc , char * * argv , std : : ostream & os ) ;
extern CommandLine : : CLIStatus rlcTest ( int argc , char * * argv , std : : ostream & os ) ;
} ;
namespace SGSN {
// Hack.
extern CommandLine : : CLIStatus sgsnCLI ( int argc , char * * argv , std : : ostream & os ) ;
} ;
# include <Globals.h>
# include "CLI.h"
# undef WARNING
extern TransceiverManager gTRX ;
//extern NodeManager gNodeManager;
namespace CommandLine {
using namespace std ;
using namespace Control ;
/** 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
} ;
struct CLIParseError {
string msg ;
CLIParseError ( string wMsg ) : msg ( wMsg ) { }
} ;
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 ;
}
CLIStatus Parser : : process ( const char * line , ostream & os ) const
{
static Mutex oneCommandAtATime ;
ScopedLock lock ( oneCommandAtATime ) ;
char * newLine = strdup ( line ) ;
CLIStatus retVal = execute ( newLine , os ) ;
free ( newLine ) ;
if ( retVal < 0 | | retVal > FAILURE ) {
os < < " Unrecognized CLI command exit status: " < < retVal < < endl ;
} else if ( retVal ! = SUCCESS ) {
os < < standardResponses [ retVal ] < < endl ;
}
return retVal ;
}
static void * commandLineFunc ( void * arg )
{
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 ( gParser . 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 ( gParser . 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");
}
printf ( " ALERT: exiting OpenBTS as directed by command line; exiting... \n " ) ;
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 ( commandLineFunc , NULL ) ;
}
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 ( ) ;
}
// 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
static 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 ; } // Thats the end of that.
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.
}
}
// 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 ;
}
/**@name Commands for the CLI. */
//@{
// forward refs
static CLIStatus printStats ( int argc , char * * argv , ostream & os ) ;
/*
A CLI command takes the argument in an array .
It returns 0 on success .
*/
/** Display system uptime and current GSM frame number. */
static CLIStatus uptime ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
os . precision ( 2 ) ;
time_t now = time ( NULL ) ;
const char * timestring = ctime ( & now ) ;
// no endl since ctime includes a "\n" in the string
os < < " Unix time " < < now < < " , " < < timestring ;
int seconds = gNodeB . uptime ( ) ;
if ( seconds < 120 ) {
os < < " uptime " < < seconds < < " seconds, frame " < < gNodeB . time ( ) < < endl ;
return SUCCESS ;
}
float minutes = seconds / 60.0F ;
if ( minutes < 120 ) {
os < < " uptime " < < minutes < < " minutes, frame " < < gNodeB . time ( ) < < endl ;
return SUCCESS ;
}
float hours = minutes / 60.0F ;
if ( hours < 48 ) {
os < < " uptime " < < hours < < " hours, frame " < < gNodeB . time ( ) < < endl ;
return SUCCESS ;
}
float days = hours / 24.0F ;
os < < " uptime " < < days < < " days, frame " < < gNodeB . time ( ) < < endl ;
return SUCCESS ;
}
/** Give a list of available commands or describe a specific command. */
static CLIStatus showHelp ( int argc , char * * argv , ostream & os )
{
if ( argc = = 2 ) {
os < < argv [ 1 ] < < " " < < gParser . help ( argv [ 1 ] ) < < endl ;
return SUCCESS ;
}
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
ParseTable : : const_iterator cp = gParser . 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 ! = gParser . 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 ;
return SUCCESS ;
}
/** A function to return -1, the exit code for the caller. */
static CLIStatus exit_function ( int argc , char * * argv , ostream & os )
{
unsigned wait = 0 ;
if ( argc > 2 ) return BAD_NUM_ARGS ;
if ( argc = = 2 ) wait = atoi ( argv [ 1 ] ) ;
if ( wait ! = 0 )
os < < " waiting up to " < < wait < < " seconds for clearing of "
< < gNodeB . DTCHActive ( ) < < " active calls " < < endl ;
// Block creation of new channels.
gNodeB . hold ( true ) ;
// Wait up to the timeout for active channels to release.
time_t finish = time ( NULL ) + wait ;
while ( time ( NULL ) < finish ) {
unsigned load = gNodeB . DTCHActive ( ) ;
if ( load = = 0 ) break ;
sleep ( 1 ) ;
}
//bool loads = false;
if ( gNodeB . DTCHActive ( ) > 0 ) {
LOG ( WARNING ) < < " dropping " < < gNodeB . DTCHActive ( ) < < " DTCH channels on exit " ;
//loads = true;
}
//if (loads) {
// os << endl << "exiting with loads:" << endl;
// printStats(1,NULL,os);
//}
os < < endl < < " exiting... " < < endl ;
return CLI_EXIT ;
}
/** Print or clear the TMSI table. */
static const char * tmsisHelp = " [-l | clear | dump [-l] <filename> | -delete -tmsi <tmsi> | -delete -imsi <imsi> | -query <query>] -- \n "
" default print the TMSI table; -l gives longer listing; \n "
" dump - dump the TMSI table to specified filename; \n "
" clear - clear the TMSI table; \n "
" -delete - delete entry for specified imsi or tmsi; \n "
" -query - run sql query, which may be quoted, eg: tmsis -query \" UPDATE TMSI_TABLE SET AUTH=0 WHERE IMSI=='123456789012' \" This option may be removed in future. "
;
/*
static CLIStatus tmsis ( int argc , char * * argv , ostream & os )
{
map < string , string > options = cliParse ( argc , argv , os , " -l dump: clear -delete -imsi: -tmsi: -query: " ) ;
if ( argc ) return BAD_NUM_ARGS ;
if ( options . count ( " clear " ) ) {
os < < " clearing TMSI table " < < endl ;
gTMSITable . tmsiTabClear ( ) ;
return SUCCESS ;
}
if ( options . count ( " dump " ) ) {
ofstream fileout ;
string filename = options [ " dump " ] ;
os < < " dumping TMSI table to " < < filename < < endl ;
fileout . open ( filename . c_str ( ) , ios : : out ) ; // erases existing!
gTMSITable . tmsiTabDump ( options . count ( " -l " ) , fileout ) ;
return SUCCESS ;
//return dumpTMSIs(filename.c_str());
}
if ( options . count ( " -delete " ) ) {
string tmsiopt = options [ " -tmsi " ] ;
string imsiopt = options [ " -imsi " ] ;
if ( tmsiopt . size ( ) ) {
unsigned tmsi = strtoul ( tmsiopt . c_str ( ) , NULL , 0 ) ;
if ( gTMSITable . dropTmsi ( tmsi ) ) {
os < < format ( " Deleted TMSI table entry for 0x%x " , tmsi ) < < endl ;
} else {
os < < format ( " Cound not delete TMSI table entry for 0x%x " , tmsi ) < < endl ;
return FAILURE ;
}
} else if ( imsiopt . size ( ) ) {
if ( gTMSITable . dropImsi ( imsiopt . c_str ( ) ) ) {
os < < format ( " Deleted TMSI table entry for %s " , imsiopt ) < < endl ;
} else {
os < < format ( " Cound not delete TMSI table entry for %s " , imsiopt ) < < endl ;
return FAILURE ;
}
} else {
os < < " tmsis delete expecting: tmsi or imsi value " < < endl ;
return FAILURE ;
}
return SUCCESS ;
}
if ( options . count ( " -query " ) ) {
string query = options [ " -query " ] ;
if ( gTMSITable . runQuery ( query . c_str ( ) , 0 ) ) {
os < < " Query success. " < < endl ;
return SUCCESS ;
} else {
os < < " Query failed: " < < query < < endl ;
return FAILURE ;
}
}
gTMSITable . tmsiTabDump ( options . count ( " -l " ) , os ) ;
return SUCCESS ;
}
*/
int isIMSI ( const char * imsi )
{
if ( ! imsi )
return 0 ;
if ( strlen ( imsi ) ! = 15 )
return 0 ;
for ( unsigned i = 0 ; i < strlen ( imsi ) ; i + + ) {
if ( ! isdigit ( imsi [ i ] ) )
return 0 ;
}
return 1 ;
}
/** Submit an SMS for delivery to an IMSI. */
static CLIStatus sendsimple ( int argc , char * * argv , ostream & os )
{
if ( argc < 4 ) return BAD_NUM_ARGS ;
char * IMSI = argv [ 1 ] ;
char * srcAddr = argv [ 2 ] ;
string rest = " " ;
for ( int i = 3 ; i < argc ; i + + ) rest = rest + argv [ i ] + " " ;
const char * txtBuf = rest . c_str ( ) ;
if ( ! isIMSI ( IMSI ) ) {
os < < " Invalid IMSI. Enter 15 digits only. " ;
return BAD_VALUE ;
}
static UDPSocket sock ( 0 , " 127.0.0.1 " , gConfig . getNum ( " SIP.Local.Port " ) ) ;
static const char form [ ] =
" MESSAGE sip:IMSI%s@127.0.0.1 SIP/2.0 \n "
" Via: SIP/2.0/TCP 127.0.0.1;branch=%x \n "
" Max-Forwards: 2 \n "
" From: %s <sip:%s@127.0.0.1:%d>;tag=%d \n "
" To: sip:IMSI%s@127.0.0.1 \n "
" Call-ID: %x@127.0.0.1:%d \n "
" CSeq: 1 MESSAGE \n "
" Content-Type: text/plain \n Content-Length: %u \n "
" \n %s \n " ;
static char buffer [ 1500 ] ;
snprintf ( buffer , 1499 , form ,
IMSI , ( unsigned ) random ( ) , srcAddr , srcAddr , sock . port ( ) , ( unsigned ) random ( ) , IMSI , ( unsigned ) random ( ) , sock . port ( ) , strlen ( txtBuf ) , txtBuf ) ;
sock . write ( buffer ) ;
os < < " message submitted for delivery " < < endl ;
#if 0
int numRead = sock . read ( buffer , 10000 ) ;
if ( numRead > = 0 ) {
buffer [ numRead ] = ' \0 ' ;
os < < " response: " < < buffer < < endl ;
} else {
os < < " timed out waiting for response " ;
}
# endif
return SUCCESS ;
}
static CLIStatus cellID ( int argc , char * * argv , ostream & os )
{
if ( argc = = 1 ) {
os < < " MCC= " < < gConfig . getStr ( " UMTS.Identity.MCC " )
< < " MNC= " < < gConfig . getStr ( " UMTS.Identity.MNC " )
< < " LAC= " < < gConfig . getNum ( " UMTS.Identity.LAC " )
< < " CI= " < < gConfig . getNum ( " UMTS.Identity.CI " )
< < endl ;
return SUCCESS ;
}
if ( argc ! = 5 ) return BAD_NUM_ARGS ;
// Safety check the args!!
if ( ! gConfig . isValidValue ( " UMTS.Identity.MCC " , argv [ 1 ] ) ) {
os < < " MCC must be three digits " < < endl ;
return BAD_VALUE ;
}
if ( ! gConfig . isValidValue ( " UMTS.Identity.MNC " , argv [ 2 ] ) ) {
os < < " MNC must be two or three digits " < < endl ;
return BAD_VALUE ;
}
if ( ! gConfig . isValidValue ( " UMTS.Identity.LAC " , argv [ 3 ] ) ) {
os < < " Invalid value for LAC " < < endl ;
return BAD_VALUE ;
}
if ( ! gConfig . isValidValue ( " UMTS.Identity.CI " , argv [ 4 ] ) ) {
os < < " Invalid value for CI " < < endl ;
return BAD_VALUE ;
}
// gTMSITable.tmsiTabClear();
gConfig . set ( " UMTS.Identity.MCC " , argv [ 1 ] ) ;
gConfig . set ( " UMTS.Identity.MNC " , argv [ 2 ] ) ;
gConfig . set ( " UMTS.Identity.LAC " , argv [ 3 ] ) ;
gConfig . set ( " UMTS.Identity.CI " , argv [ 4 ] ) ;
return SUCCESS ;
}
/** Print or modify the global configuration table. */
static CLIStatus rawconfig ( int argc , char * * argv , ostream & os )
{
// no args, just print
if ( argc = = 1 ) {
gConfig . find ( " " , os ) ;
return SUCCESS ;
}
// one arg, pattern match and print
if ( argc = = 2 ) {
gConfig . find ( argv [ 1 ] , os ) ;
return SUCCESS ;
}
// >1 args: set new value
string val ;
for ( int i = 2 ; i < argc ; i + + ) {
val . append ( argv [ i ] ) ;
if ( i ! = ( argc - 1 ) ) val . append ( " " ) ;
}
bool existing = gConfig . defines ( argv [ 1 ] ) ;
string previousVal ;
if ( existing ) {
previousVal = gConfig . getStr ( argv [ 1 ] ) ;
}
if ( ! gConfig . set ( argv [ 1 ] , val ) ) {
os < < " DB ERROR: " < < argv [ 1 ] < < " change failed " < < endl ;
return FAILURE ;
}
if ( gConfig . isStatic ( argv [ 1 ] ) ) {
os < < argv [ 1 ] < < " is static; change takes effect on restart " < < endl ;
}
if ( ! existing ) {
os < < " defined new config " < < argv [ 1 ] < < " as \" " < < val < < " \" " < < endl ;
} else {
os < < argv [ 1 ] < < " changed from \" " < < previousVal < < " \" to \" " < < val < < " \" " < < endl ;
}
return SUCCESS ;
}
/* TODO : add getFactoryCalibration to TranceiverRAD1
static CLIStatus trxfactory ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
signed val = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " sdrsn " ) ;
if ( val = = 0 | | val = = 65535 ) {
os < < " Reading factory calibration not supported on this radio. " < < endl ;
return SUCCESS ;
}
os < < " Factory Information " < < endl ;
os < < " SDR Serial Number = " < < val < < endl ;
val = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " rfsn " ) ;
os < < " RF Serial Number = " < < val < < endl ;
val = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " band " ) ;
os < < " UMTS.Radio.Band = " ;
if ( val = = 0 ) {
os < < " multi-band " ;
} else {
os < < val ;
}
os < < endl ;
val = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " rxgain " ) ;
os < < " UMTS.Radio.RxGain = " < < val < < endl ;
val = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " txgain " ) ;
os < < " TRX.TxAttenOffset = " < < val < < endl ;
val = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " freq " ) ;
os < < " TRX.RadioFrequencyOffset = " < < val < < endl ;
return SUCCESS ;
}
*/
/** Audit the current configuration. */
static CLIStatus audit ( int argc , char * * argv , ostream & os )
{
ConfigurationKeyMap : : iterator mp ;
stringstream ss ;
// value errors
mp = gConfig . mSchema . begin ( ) ;
while ( mp ! = gConfig . mSchema . end ( ) ) {
if ( ! gConfig . isValidValue ( mp - > first , gConfig . getStr ( mp - > first ) ) ) {
ss < < mp - > first < < " \" " < < gConfig . getStr ( mp - > first ) < < " \" ( \" " < < mp - > second . getDefaultValue ( ) < < " \" ) " < < endl ;
}
mp + + ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | ERROR : Invalid Values [key current-value (default)] | " < < endl ;
os < < " | To use the default value again, execute: rmconfig key | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
/* TODO : add getFactoryCalibration to TranceiverRAD1
// factory calibration warnings
signed sdrsn = gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " sdrsn " ) ;
if ( sdrsn ! = 0 & & sdrsn ! = 65535 ) {
string factoryValue ;
string configValue ;
factoryValue = gConfig . mSchema [ " UMTS.Radio.Band " ] . getDefaultValue ( ) ;
configValue = gConfig . getStr ( " UMTS.Radio.Band " ) ;
// only warn on band changes if the unit is not multi-band
if ( gTRX . ARFCN ( 0 ) - > getFactoryCalibration ( " band " ) ! = 0 & & configValue . compare ( factoryValue ) ! = 0 ) {
ss < < " UMTS.Radio.Band \" " < < configValue < < " \" ( \" " < < factoryValue < < " \" ) " < < endl ;
}
factoryValue = gConfig . mSchema [ " UMTS.Radio.RxGain " ] . getDefaultValue ( ) ;
configValue = gConfig . getStr ( " UMTS.Radio.RxGain " ) ;
if ( configValue . compare ( factoryValue ) ! = 0 ) {
ss < < " UMTS.Radio.RxGain \" " < < configValue < < " \" ( \" " < < factoryValue < < " \" ) " < < endl ;
}
factoryValue = gConfig . mSchema [ " TRX.TxAttenOffset " ] . getDefaultValue ( ) ;
configValue = gConfig . getStr ( " TRX.TxAttenOffset " ) ;
if ( configValue . compare ( factoryValue ) ! = 0 ) {
ss < < " TRX.TxAttenOffset \" " < < configValue < < " \" ( \" " < < factoryValue < < " \" ) " < < endl ;
}
factoryValue = gConfig . mSchema [ " TRX.RadioFrequencyOffset " ] . getDefaultValue ( ) ;
configValue = gConfig . getStr ( " TRX.RadioFrequencyOffset " ) ;
if ( configValue . compare ( factoryValue ) ! = 0 ) {
ss < < " TRX.RadioFrequencyOffset \" " < < configValue < < " \" ( \" " < < factoryValue < < " \" ) " < < endl ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | WARNING : Factory Radio Calibration [key current-value (factory)] | " < < endl ;
os < < " | To use the factory value again, execute: rmconfig key | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
}
*/
// cross check warnings
vector < string > allWarnings ;
vector < string > warnings ;
vector < string > : : iterator warning ;
mp = gConfig . mSchema . begin ( ) ;
while ( mp ! = gConfig . mSchema . end ( ) ) {
warnings = gConfig . crossCheck ( mp - > first ) ;
allWarnings . insert ( allWarnings . end ( ) , warnings . begin ( ) , warnings . end ( ) ) ;
mp + + ;
}
sort ( allWarnings . begin ( ) , allWarnings . end ( ) ) ;
allWarnings . erase ( unique ( allWarnings . begin ( ) , allWarnings . end ( ) ) , allWarnings . end ( ) ) ;
warning = allWarnings . begin ( ) ;
while ( warning ! = allWarnings . end ( ) ) {
ss < < * warning < < endl ;
warning + + ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | WARNING : Cross-Check Values | " < < endl ;
os < < " | To quiet these warnings, follow the advice given. | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
// site-specific values
mp = gConfig . mSchema . begin ( ) ;
while ( mp ! = gConfig . mSchema . end ( ) ) {
if ( mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERSITE ) {
if ( gConfig . getStr ( mp - > first ) . compare ( gConfig . mSchema [ mp - > first ] . getDefaultValue ( ) ) = = 0 ) {
ss < < mp - > first < < " \" " < < gConfig . mSchema [ mp - > first ] . getDefaultValue ( ) < < " \" " < < endl ;
}
}
mp + + ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | WARNING : Site Values Which Are Still Default [key current-value] | " < < endl ;
os < < " | These should be set to fit your installation: config key value | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
/* TODO : gNodeManager
// unapplied values
std : : map < std : : string , std : : string > dirtyKeys = gNodeManager . getDirtyConfigurationKeysMap ( ) ;
std : : map < std : : string , std : : string > : : iterator dk = dirtyKeys . begin ( ) ;
while ( dk ! = dirtyKeys . end ( ) ) {
ss < < dk - > first < < " \" " < < gConfig . getStr ( dk - > first ) < < " \" ( \" " < < dk - > second < < " \" ) " < < endl ;
dk + + ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | WARNING : Unapplied Changes [key desired-value (running-value)] | " < < endl ;
os < < " | To apply values, restart this daemon by executing: shutdown | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
*/
// non-default values
mp = gConfig . mSchema . begin ( ) ;
while ( mp ! = gConfig . mSchema . end ( ) ) {
if ( mp - > second . getVisibility ( ) ! = ConfigurationKey : : CUSTOMERSITE ) {
if ( gConfig . getStr ( mp - > first ) . compare ( gConfig . mSchema [ mp - > first ] . getDefaultValue ( ) ) ! = 0 ) {
ss < < mp - > first < < " \" " < < gConfig . getStr ( mp - > first ) < < " \" ( \" " < < mp - > second . getDefaultValue ( ) < < " \" ) " < < endl ;
}
}
mp + + ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | INFO : Non-Default Values [key current-value (default)] | " < < endl ;
os < < " | To use the default value again, execute: rmconfig key | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
// unknown pairs
ConfigurationRecordMap pairs = gConfig . getAllPairs ( ) ;
ConfigurationRecordMap : : iterator mp2 = pairs . begin ( ) ;
while ( mp2 ! = pairs . end ( ) ) {
if ( ! gConfig . keyDefinedInSchema ( mp2 - > first ) ) {
// also kindly ignore SIM.Prog keys for now so the users don't kill their ability to program SIMs
string family = " SIM.Prog. " ;
if ( mp2 - > first . substr ( 0 , family . size ( ) ) ! = family ) {
ss < < mp2 - > first < < " \" " < < mp2 - > second . value ( ) < < " \" " < < endl ;
}
}
mp2 + + ;
}
if ( ss . str ( ) . length ( ) ) {
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < " | INFO : Custom/Deprecated Key/Value Pairs [key current-value] | " < < endl ;
os < < " | To clean up any extraneous keys, execute: rmconfig key | " < < endl ;
os < < " +---------------------------------------------------------------------+ " < < endl ;
os < < ss . str ( ) ;
os < < endl ;
ss . str ( " " ) ;
}
return SUCCESS ;
}
/** Print or modify the global configuration table. */
CLIStatus _config ( string mode , int argc , char * * argv , ostream & os )
{
// no args, just print
if ( argc = = 1 ) {
ConfigurationKeyMap : : iterator mp = gConfig . mSchema . begin ( ) ;
while ( mp ! = gConfig . mSchema . end ( ) ) {
if ( mode . compare ( " customer " ) = = 0 ) {
if ( mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMER | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERSITE | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERTUNE | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERWARN ) {
ConfigurationKey : : printKey ( mp - > second , gConfig . getStr ( mp - > first ) , os ) ;
}
} else if ( mode . compare ( " developer " ) = = 0 ) {
ConfigurationKey : : printKey ( mp - > second , gConfig . getStr ( mp - > first ) , os ) ;
}
mp + + ;
}
return SUCCESS ;
}
// one arg
if ( argc = = 2 ) {
// matches exactly? print single key
if ( gConfig . keyDefinedInSchema ( argv [ 1 ] ) ) {
ConfigurationKey : : printKey ( gConfig . mSchema [ argv [ 1 ] ] , gConfig . getStr ( argv [ 1 ] ) , os ) ;
ConfigurationKey : : printDescription ( gConfig . mSchema [ argv [ 1 ] ] , os ) ;
os < < endl ;
// ...otherwise print all similar keys
} else {
int foundCount = 0 ;
ConfigurationKeyMap matches = gConfig . getSimilarKeys ( argv [ 1 ] ) ;
ConfigurationKeyMap : : iterator mp = matches . begin ( ) ;
while ( mp ! = matches . end ( ) ) {
if ( mode . compare ( " customer " ) = = 0 ) {
if ( mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMER | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERSITE | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERTUNE | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERWARN ) {
ConfigurationKey : : printKey ( mp - > second , gConfig . getStr ( mp - > first ) , os ) ;
foundCount + + ;
}
} else if ( mode . compare ( " developer " ) = = 0 ) {
ConfigurationKey : : printKey ( mp - > second , gConfig . getStr ( mp - > first ) , os ) ;
foundCount + + ;
}
mp + + ;
}
if ( ! foundCount ) {
os < < argv [ 1 ] < < " - no keys matched " ;
if ( mode . compare ( " customer " ) = = 0 ) {
os < < " , developer/factory keys can be accessed with \" devconfig. \" " ;
} else if ( mode . compare ( " developer " ) = = 0 ) {
os < < " , custom keys can be accessed with \" rawconfig. \" " ;
}
os < < endl ;
}
}
return SUCCESS ;
}
// >1 args: set new value
string val ;
for ( int i = 2 ; i < argc ; i + + ) {
val . append ( argv [ i ] ) ;
if ( i ! = ( argc - 1 ) ) val . append ( " " ) ;
}
if ( ! gConfig . keyDefinedInSchema ( argv [ 1 ] ) ) {
os < < argv [ 1 ] < < " is not a valid key, change failed. If you're trying to define a custom key/value pair (e.g. the Log.Level.Filename.cpp pairs), use \" rawconfig. \" " < < endl ;
return SUCCESS ;
}
if ( mode . compare ( " customer " ) = = 0 ) {
if ( gConfig . mSchema [ argv [ 1 ] ] . getVisibility ( ) = = ConfigurationKey : : DEVELOPER ) {
os < < argv [ 1 ] < < " should only be changed by developers. Use \" devconfig \" if you are ABSOLUTELY sure this needs to be changed. " < < endl ;
return SUCCESS ;
}
if ( gConfig . mSchema [ argv [ 1 ] ] . getVisibility ( ) = = ConfigurationKey : : FACTORY ) {
os < < argv [ 1 ] < < " should only be set once by the factory. Use \" devconfig \" if you are ABSOLUTELY sure this needs to be changed. " < < endl ;
return SUCCESS ;
}
}
if ( ! gConfig . isValidValue ( argv [ 1 ] , val ) ) {
os < < argv [ 1 ] < < " new value \" " < < val < < " \" is invalid, change failed. " ;
if ( mode . compare ( " developer " ) = = 0 ) {
os < < " To override the configuration value checks, use \" rawconfig. \" " ;
}
os < < endl ;
return SUCCESS ;
}
string previousVal = gConfig . getStr ( argv [ 1 ] ) ;
if ( val . compare ( previousVal ) = = 0 ) {
os < < argv [ 1 ] < < " is already set to \" " < < val < < " \" , nothing changed " < < endl ;
return SUCCESS ;
}
// TODO : removing of default values from DB disabled for now. Breaks webui.
// if (val.compare(gConfig.mSchema[argv[1]].getDefaultValue()) == 0) {
// if (!gConfig.remove(argv[1])) {
// os << argv[1] << " storing new value (default) failed" << endl;
// return SUCCESS;
// }
// } else {
if ( ! gConfig . set ( argv [ 1 ] , val ) ) {
os < < " DB ERROR: " < < argv [ 1 ] < < " could not be updated " < < endl ;
return FAILURE ;
}
// }
// TODO : need to re-implement getARFCNsString() to provide info for UMTS
//if (string(argv[1]).compare("UMTS.Radio.Band") == 0) {
// gConfig.mSchema["UMTS.Radio.C0"].updateValidValues(getARFCNsString(gConfig.getNum("UMTS.Radio.Band")));
//}
vector < string > warnings = gConfig . crossCheck ( argv [ 1 ] ) ;
vector < string > : : iterator warning = warnings . begin ( ) ;
while ( warning ! = warnings . end ( ) ) {
os < < " WARNING: " < < * warning < < endl ;
warning + + ;
}
if ( gConfig . isStatic ( argv [ 1 ] ) ) {
os < < argv [ 1 ] < < " is static; change takes effect on restart " < < endl ;
}
os < < argv [ 1 ] < < " changed from \" " < < previousVal < < " \" to \" " < < val < < " \" " < < endl ;
return SUCCESS ;
}
/** Print or modify the global configuration table. Customer access. */
static CLIStatus config ( int argc , char * * argv , ostream & os )
{
return _config ( " customer " , argc , argv , os ) ;
}
/** Print or modify the global configuration table. Developer/factory access. */
static CLIStatus devconfig ( int argc , char * * argv , ostream & os )
{
return _config ( " developer " , argc , argv , os ) ;
}
/** Disable a configuration key. */
static CLIStatus unconfig ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 2 ) return BAD_NUM_ARGS ;
if ( ! gConfig . defines ( argv [ 1 ] ) ) {
os < < argv [ 1 ] < < " is not in the table " < < endl ;
return BAD_VALUE ;
}
if ( gConfig . keyDefinedInSchema ( argv [ 1 ] ) & & ! gConfig . isValidValue ( argv [ 1 ] , " " ) ) {
os < < argv [ 1 ] < < " is not disableable " < < endl ;
return BAD_VALUE ;
}
if ( ! gConfig . set ( argv [ 1 ] , " " ) ) {
os < < " DB ERROR: " < < argv [ 1 ] < < " could not be disabled " < < endl ;
return FAILURE ;
}
os < < argv [ 1 ] < < " disabled " < < endl ;
return SUCCESS ;
}
/** Set a configuration value back to default or remove from table if custom key. */
static CLIStatus rmconfig ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 2 ) return BAD_NUM_ARGS ;
if ( ! gConfig . defines ( argv [ 1 ] ) ) {
os < < argv [ 1 ] < < " is not in the table " < < endl ;
return BAD_VALUE ;
}
// TODO : removing of default values from DB disabled for now. Breaks webui.
if ( gConfig . keyDefinedInSchema ( argv [ 1 ] ) ) {
if ( ! gConfig . set ( argv [ 1 ] , gConfig . mSchema [ argv [ 1 ] ] . getDefaultValue ( ) ) ) {
os < < " DB ERROR: " < < argv [ 1 ] < < " could not be set back to the default value " < < endl ;
return FAILURE ;
}
os < < argv [ 1 ] < < " set back to its default value " < < endl ;
vector < string > warnings = gConfig . crossCheck ( argv [ 1 ] ) ;
vector < string > : : iterator warning = warnings . begin ( ) ;
while ( warning ! = warnings . end ( ) ) {
os < < " WARNING: " < < * warning < < endl ;
warning + + ;
}
if ( gConfig . isStatic ( argv [ 1 ] ) ) {
os < < argv [ 1 ] < < " is static; change takes effect on restart " < < endl ;
}
return SUCCESS ;
}
if ( ! gConfig . remove ( argv [ 1 ] ) ) {
os < < " DB ERROR: " < < argv [ 1 ] < < " could not be removed from the configuration table " < < endl ;
return FAILURE ;
}
os < < argv [ 1 ] < < " removed from the configuration table " < < endl ;
return SUCCESS ;
}
/** Change the registration timers. */
static CLIStatus regperiod ( int argc , char * * argv , ostream & os )
{
if ( argc = = 1 ) {
os < < " T3212 is " < < gConfig . getNum ( " GSM.Timer.T3212 " ) < < " minutes " < < endl ;
os < < " SIP registration period is " < < gConfig . getNum ( " SIP.RegistrationPeriod " ) < < " minutes " < < endl ;
return SUCCESS ;
}
if ( argc > 3 ) return BAD_NUM_ARGS ;
unsigned newT3212 = strtol ( argv [ 1 ] , NULL , 10 ) ;
if ( ! gConfig . isValidValue ( " UMTS.Timer.T3212 " , argv [ 1 ] ) ) {
os < < " valid T3212 range is 6..1530 minutes " < < endl ;
return BAD_VALUE ;
}
// By default, make SIP registration period 1.5x the GSM registration period.
unsigned SIPRegPeriod = newT3212 * 1.5 ;
char SIPRegPeriodStr [ 10 ] ;
sprintf ( SIPRegPeriodStr , " %u " , SIPRegPeriod ) ;
if ( argc = = 3 ) {
SIPRegPeriod = strtol ( argv [ 2 ] , NULL , 10 ) ;
sprintf ( SIPRegPeriodStr , " %s " , argv [ 2 ] ) ;
}
if ( ! gConfig . isValidValue ( " SIP.RegistrationPeriod " , SIPRegPeriodStr ) ) {
os < < " valid SIP registration range is 6..2298 minutes " < < endl ;
return BAD_VALUE ;
}
// Set the values in the table and on the GSM beacon.
gConfig . set ( " SIP.RegistrationPeriod " , SIPRegPeriod ) ;
gConfig . set ( " UMTS.Timer.T3212 " , newT3212 ) ;
// Done.
return SUCCESS ;
}
/** Print the list of alarms kept by the logger, i.e. the last LOG(ALARM) << <text> */
static CLIStatus alarms ( int argc , char * * argv , ostream & os )
{
std : : ostream_iterator < std : : string > output ( os , " \n " ) ;
std : : list < std : : string > alarms = gGetLoggerAlarms ( ) ;
std : : copy ( alarms . begin ( ) , alarms . end ( ) , output ) ;
return SUCCESS ;
}
/** Version string. */
static CLIStatus version ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
os < < gVersionString < < endl ;
return SUCCESS ;
}
/** Show start-up notices. */
static CLIStatus notices ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
os < < endl < < gOpenWelcome < < endl ;
return SUCCESS ;
}
static CLIStatus page ( int argc , char * * argv , ostream & os )
{
if ( argc = = 1 ) {
gNodeB . pager ( ) . dump ( os ) ;
return SUCCESS ;
}
if ( argc ! = 3 ) return BAD_NUM_ARGS ;
char * IMSI = argv [ 1 ] ;
if ( strlen ( IMSI ) > 15 ) {
os < < IMSI < < " is not a valid IMSI " < < endl ;
return BAD_VALUE ;
}
Control : : TransactionEntry dummy (
gConfig . getStr ( " SIP.Proxy.SMS " ) . c_str ( ) ,
GSM : : L3MobileIdentity ( IMSI ) ,
NULL ,
GSM : : L3CMServiceType : : UndefinedType ,
GSM : : L3CallingPartyBCDNumber ( " 0 " ) ,
GSM : : Paging ) ;
gNodeB . pager ( ) . addID ( GSM : : L3MobileIdentity ( IMSI ) , UMTS : : DCCHType , dummy , 1000 * atoi ( argv [ 2 ] ) ) ;
return SUCCESS ;
}
static CLIStatus endcall ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 2 ) return BAD_NUM_ARGS ;
unsigned transID = atoi ( argv [ 1 ] ) ;
Control : : TransactionEntry * target = gTransactionTable . find ( transID ) ;
if ( ! target ) {
os < < transID < < " not found in table " ;
return BAD_VALUE ;
}
target - > terminate ( ) ;
return SUCCESS ;
}
static CLIStatus power ( int argc , char * * argv , ostream & os )
{
// os << "current downlink power " << gNodeB.powerManager().power() << " dB wrt full scale" << endl;
os < < " current attenuation bounds "
2016-08-30 09:53:58 -07:00
< < gConfig . getNum ( " UMTS.Radio.PowerManager.MinAttenDB " )
2014-10-16 14:42:05 -07:00
< < " to "
2016-08-30 09:53:58 -07:00
< < gConfig . getNum ( " UMTS.Radio.PowerManager.MaxAttenDB " )
2014-10-16 14:42:05 -07:00
< < " dB " < < endl ;
if ( argc = = 1 ) return SUCCESS ;
if ( argc ! = 3 ) return BAD_NUM_ARGS ;
int min = atoi ( argv [ 1 ] ) ;
int max = atoi ( argv [ 2 ] ) ;
if ( min > max ) {
os < < " Min is larger than max " < < endl ;
return BAD_VALUE ;
}
2016-08-30 09:53:58 -07:00
if ( ! gConfig . isValidValue ( " UMTS.Radio.PowerManager.MinAttenDB " , argv [ 1 ] ) ) {
2014-10-16 14:42:05 -07:00
os < < " Invalid new value for min. It must be in range ( " ;
2016-08-30 09:53:58 -07:00
os < < gConfig . mSchema [ " UMTS.Radio.PowerManager.MinAttenDB " ] . getValidValues ( ) < < " ) " < < endl ;
2014-10-16 14:42:05 -07:00
return BAD_VALUE ;
}
2016-08-30 09:53:58 -07:00
if ( ! gConfig . isValidValue ( " UMTS.Radio.PowerManager.MaxAttenDB " , argv [ 2 ] ) ) {
2014-10-16 14:42:05 -07:00
os < < " Invalid new value for max. It must be in range ( " ;
2016-08-30 09:53:58 -07:00
os < < gConfig . mSchema [ " UMTS.Radio.PowerManager.MaxAttenDB " ] . getValidValues ( ) < < " ) " < < endl ;
2014-10-16 14:42:05 -07:00
return BAD_VALUE ;
}
2016-08-30 09:53:58 -07:00
gConfig . set ( " UMTS.Radio.PowerManager.MinAttenDB " , argv [ 1 ] ) ;
gConfig . set ( " UMTS.Radio.PowerManager.MaxAttenDB " , argv [ 2 ] ) ;
2014-10-16 14:42:05 -07:00
os < < " new attenuation bounds "
2016-08-30 09:53:58 -07:00
< < gConfig . getNum ( " UMTS.Radio.PowerManager.MinAttenDB " )
2014-10-16 14:42:05 -07:00
< < " to "
2016-08-30 09:53:58 -07:00
< < gConfig . getNum ( " UMTS.Radio.PowerManager.MaxAttenDB " )
2014-10-16 14:42:05 -07:00
< < " dB " < < endl ;
return SUCCESS ;
}
static CLIStatus rxgain ( int argc , char * * argv , ostream & os )
{
os < < " current RX gain is " < < gConfig . getNum ( " UMTS.Radio.RxGain " ) < < " dB " < < endl ;
if ( argc = = 1 ) return SUCCESS ;
if ( argc ! = 2 ) return BAD_NUM_ARGS ;
if ( ! gConfig . isValidValue ( " GSM.Radio.RxGain " , argv [ 1 ] ) ) {
os < < " Invalid new value for RX gain. It must be in range ( " ;
os < < gConfig . mSchema [ " GSM.Radio.RxGain " ] . getValidValues ( ) < < " ) " < < endl ;
return BAD_VALUE ;
}
/* TODO : gTRX.ARFCN(0)->setRxGain
int newGain = gTRX . ARFCN ( 0 ) - > setRxGain ( atoi ( argv [ 1 ] ) ) ;
os < < " new RX gain is " < < newGain < < " dB " < < endl ;
gConfig . set ( " UMTS.Radio.RxGain " , newGain ) ;
*/
return SUCCESS ;
}
static CLIStatus txatten ( int argc , char * * argv , ostream & os )
{
os < < " current TX attenuation is " < < gConfig . getNum ( " TRX.TxAttenOffset " ) < < " dB " < < endl ;
if ( argc = = 1 ) return SUCCESS ;
if ( argc ! = 2 ) return BAD_NUM_ARGS ;
if ( ! gConfig . isValidValue ( " TRX.TxAttenOffset " , argv [ 1 ] ) ) {
os < < " Invalid new value for TX attenuation. It must be in range ( " ;
os < < gConfig . mSchema [ " TRX.TxAttenOffset " ] . getValidValues ( ) < < " ) " < < endl ;
return BAD_VALUE ;
}
/* TODO : gTRX.ARFCN(0)->setTxAtten
int newAtten = gTRX . ARFCN ( 0 ) - > setTxAtten ( atoi ( argv [ 1 ] ) ) ;
os < < " new TX attenuation is " < < newAtten < < " dB " < < endl ;
gConfig . set ( " TRX.TxAttenOffset " , newAtten ) ;
*/
return SUCCESS ;
}
static CLIStatus temperature ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
int temperature = gTRX . ARFCN ( 0 ) - > getTemperature ( ) ;
os < < " temperature is " < < temperature < < " C " < < endl ;
return SUCCESS ;
}
/*
// TODO : re-add support for noise command, right now it's not implemented in transceiver code
static CLIStatus noise ( int argc , char * * argv , ostream & os )
{
if ( argc ! = 1 ) return BAD_NUM_ARGS ;
int noise = gTRX . ARFCN ( 0 ) - > getNoiseLevel ( ) ;
os < < " noise RSSI is - " < < noise < < " dB wrt full scale " < < endl ;
os < < " MS RSSI target is " < < gConfig . getNum ( " UMTS.Radio.RSSITarget " ) < < " dB wrt full scale " < < endl ;
return SUCCESS ;
}
*/
static CLIStatus freqcorr ( int argc , char * * argv , ostream & os )
{
os < < " current freq. offset is " < < gConfig . getNum ( " TRX.RadioFrequencyOffset " ) < < endl ;
if ( argc = = 1 ) return SUCCESS ;
if ( argc ! = 2 ) return BAD_NUM_ARGS ;
if ( ! gConfig . isValidValue ( " TRX.RadioFrequencyOffset " , argv [ 1 ] ) ) {
os < < " Invalid new value for freq. offset It must be in range ( " ;
os < < gConfig . mSchema [ " TRX.RadioFrequencyOffset " ] . getValidValues ( ) < < " ) " < < endl ;
return BAD_VALUE ;
}
int newOffset = gTRX . ARFCN ( 0 ) - > setFreqOffset ( atoi ( argv [ 1 ] ) ) ;
os < < " new freq. offset is " < < newOffset < < endl ;
gConfig . set ( " TRX.RadioFrequencyOffset " , newOffset ) ;
return SUCCESS ;
}
static CLIStatus memStat ( int argc , char * * argv , ostream & os )
{
gMemStats . text ( os ) ;
return SUCCESS ;
}
static CLIStatus crashme ( int argc , char * * argv , ostream & os )
{
char * nullp = 0x0 ;
// we actually have to output this,
// or the compiler will optimize it out
os < < * nullp ;
return FAILURE ;
}
/* TODO : stats CLI
static CLIStatus stats ( int argc , char * * argv , ostream & os )
{
char cmd [ 200 ] ;
if ( argc = = 2 ) {
if ( strcmp ( argv [ 1 ] , " clear " ) = = 0 ) {
gReports . clear ( ) ;
os < < " stats table (gReporting) cleared " < < endl ;
return SUCCESS ;
}
sprintf ( cmd , " sqlite3 %s 'select name|| \" : \" ||value|| \" events over \" ||((%lu-clearedtime)/60)|| \" minutes \" from reporting where name like \" %%%s%% \" ;' " ,
gConfig . getStr ( " Control.Reporting.StatsTable " ) . c_str ( ) , time ( NULL ) , argv [ 1 ] ) ;
}
else if ( argc = = 1 )
sprintf ( cmd , " sqlite3 %s 'select name|| \" : \" ||value|| \" events over \" ||((%lu-clearedtime)/60)|| \" minutes \" from reporting;' " ,
gConfig . getStr ( " Control.Reporting.StatsTable " ) . c_str ( ) , time ( NULL ) ) ;
else return BAD_NUM_ARGS ;
FILE * result = popen ( cmd , " r " ) ;
char * line = ( char * ) malloc ( 200 ) ;
while ( ! feof ( result ) ) {
if ( ! fgets ( line , 200 , result ) ) break ;
os < < line ;
}
free ( line ) ;
os < < endl ;
pclose ( result ) ;
return SUCCESS ;
}
*/
//@} // CLI commands
void Parser : : addCommands ( )
{
addCommand ( " uptime " , uptime , " -- show BTS uptime and BTS frame number. " ) ;
addCommand ( " help " , showHelp , " [command] -- list available commands or gets help on a specific command. " ) ;
addCommand ( " shutdown " , exit_function , " [wait] -- shut down or restart OpenBTS, either immediately, or waiting for existing calls to clear with a timeout in seconds " ) ;
addCommand ( " sendsimple " , sendsimple , " <IMSI> <src> -- send SMS to <IMSI> via SIP interface, addressed from <src>, after prompting. " ) ;
//addCommand("load", printStats, "-- print the current activity loads.");
addCommand ( " cellid " , cellID , " [MCC MNC LAC CI] -- get/set location area identity (MCC, MNC, LAC) and cell ID (CI) " ) ;
//addCommand("calls", calls, "-- print the transaction table");
addCommand ( " rawconfig " , rawconfig , " [] OR [patt] OR [key val(s)] -- print the current configuration, print configuration values matching a pattern, or set/change a configuration value " ) ;
//addCommand("trxfactory", trxfactory, "-- print the radio's factory calibration and meta information");
addCommand ( " audit " , audit , " -- audit the current configuration for troubleshooting " ) ;
addCommand ( " config " , config , " [] OR [patt] OR [key val(s)] -- print the current configuration, print configuration values matching a pattern, or set/change a configuration value " ) ;
addCommand ( " devconfig " , devconfig , " [] OR [patt] OR [key val(s)] -- print the current configuration, print configuration values matching a pattern, or set/change a configuration value " ) ;
addCommand ( " regperiod " , regperiod , " [GSM] [SIP] -- get/set the registration period (GSM T3212), in MINUTES " ) ;
addCommand ( " alarms " , alarms , " -- show latest alarms " ) ;
addCommand ( " version " , version , " -- print the version string " ) ;
addCommand ( " page " , page , " print the paging table " ) ;
addCommand ( " power " , power , " [minAtten maxAtten] -- report current attentuation or set min/max bounds " ) ;
addCommand ( " rxgain " , rxgain , " [newRxgain] -- get/set the RX gain in dB " ) ;
//addCommand("noise", noise, "-- report receive noise level in RSSI dB");
addCommand ( " temperature " , temperature , " -- report temperature level in C " ) ;
addCommand ( " unconfig " , unconfig , " key -- disable a configuration key by setting an empty value " ) ;
addCommand ( " txatten " , txatten , " [newTxAtten] -- get/set the TX attenuation in dB " ) ;
addCommand ( " freqcorr " , freqcorr , " [newOffset] -- get/set the new radio frequency offset " ) ;
addCommand ( " rmconfig " , rmconfig , " key -- set a configuration value back to its default or remove a custom key/value pair " ) ;
addCommand ( " notices " , notices , " -- show startup copyright and legal notices " ) ;
addCommand ( " sgsn " , SGSN : : sgsnCLI , " SGSN mode sub-command. Type: sgsn help for more " ) ;
addCommand ( " crashme " , crashme , " force crash of OpenBTS for testing purposes " ) ;
//addCommand("stats", stats,"[patt] OR clear -- print all, or selected, performance counters, OR clear all counters");
addCommand ( " rlctest " , UMTS : : rlcTest , " -- internal testing commands for UMTS " ) ;
addCommand ( " rrctest " , UMTS : : rrcTest , " -- internal testing commands for UMTS " ) ;
addCommand ( " memstat " , memStat , " -- internal testing command: print memory use stats " ) ;
}
} ;
// vim: ts=4 sw=4