2011-10-07 12:20:49 -07:00
/*
* Copyright 2008 , 2009 , 2010 Free Software Foundation , Inc .
* Copyright 2010 Kestrel Signal Processing , Inc .
2013-05-31 14:47:42 -07:00
* Copyright 2011 , 2012 Range Networks , Inc .
2011-10-07 12:20:49 -07:00
*
*
* This software is distributed under the terms of the GNU Affero Public License .
* See the COPYING file in the main directory for details .
*
* This use of this software may be subject to additional restrictions .
* See the LEGAL file in the main directory for details .
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
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 . See the
GNU Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "Configuration.h"
2013-05-31 14:47:42 -07:00
# include "Logger.h"
2011-10-07 12:20:49 -07:00
# include <fstream>
# include <iostream>
# include <string.h>
2013-05-31 14:47:42 -07:00
2011-10-07 12:20:49 -07:00
using namespace std ;
2013-05-31 14:47:42 -07:00
char gCmdName [ 20 ] = { 0 } ; // Use a char* to avoid avoid static initialization of string, and race at startup.
2011-10-07 12:20:49 -07:00
static const char * createConfigTable = {
" CREATE TABLE IF NOT EXISTS CONFIG ( "
" KEYSTRING TEXT UNIQUE NOT NULL, "
" VALUESTRING TEXT, "
" STATIC INTEGER DEFAULT 0, "
" OPTIONAL INTEGER DEFAULT 0, "
" COMMENTS TEXT DEFAULT '' "
" ) "
} ;
2013-05-31 14:47:42 -07:00
float ConfigurationRecord : : floatNumber ( ) const
{
float val ;
sscanf ( mValue . c_str ( ) , " %f " , & val ) ;
return val ;
}
ConfigurationTable : : ConfigurationTable ( const char * filename , const char * wCmdName , ConfigurationKeyMap wSchema )
2011-10-07 12:20:49 -07:00
{
2013-05-31 14:47:42 -07:00
gLogEarly ( LOG_INFO , " opening configuration table from path %s " , filename ) ;
2011-10-07 12:20:49 -07:00
// Connect to the database.
int rc = sqlite3_open ( filename , & mDB ) ;
2013-05-31 14:47:42 -07:00
// (pat) When I used malloc here, sqlite3 sporadically crashes.
if ( wCmdName ) {
strncpy ( gCmdName , wCmdName , 18 ) ;
gCmdName [ 18 ] = 0 ;
strcat ( gCmdName , " : " ) ;
}
2011-10-07 12:20:49 -07:00
if ( rc ) {
2013-05-31 14:47:42 -07:00
gLogEarly ( LOG_EMERG , " cannot open configuration database at %s, error message: %s " , filename , sqlite3_errmsg ( mDB ) ) ;
2011-10-07 12:20:49 -07:00
sqlite3_close ( mDB ) ;
mDB = NULL ;
return ;
}
// Create the table, if needed.
if ( ! sqlite3_command ( mDB , createConfigTable ) ) {
2013-05-31 14:47:42 -07:00
gLogEarly ( LOG_EMERG , " cannot create configuration table in database at %s, error message: %s " , filename , sqlite3_errmsg ( mDB ) ) ;
}
2013-08-13 16:17:46 -07:00
// Set high-concurrency WAL mode.
if ( ! sqlite3_command ( mDB , enableWAL ) ) {
gLogEarly ( LOG_EMERG , " cannot enable WAL mode on database at %s, error message: %s " , filename , sqlite3_errmsg ( mDB ) ) ;
}
2013-05-31 14:47:42 -07:00
// Build CommonLibs schema
ConfigurationKey * tmp ;
2013-08-13 16:17:46 -07:00
tmp = new ConfigurationKey ( " Control.NumSQLTries " , " 3 " ,
" attempts " ,
ConfigurationKey : : DEVELOPER ,
ConfigurationKey : : VALRANGE ,
" 1:10 " , // educated guess
false ,
" Number of times to retry SQL queries before declaring a database access failure. "
) ;
mSchema [ tmp - > getName ( ) ] = * tmp ;
delete tmp ;
2013-05-31 14:47:42 -07:00
tmp = new ConfigurationKey ( " Log.Alarms.Max " , " 20 " ,
" alarms " ,
ConfigurationKey : : CUSTOMER ,
ConfigurationKey : : VALRANGE ,
" 10:20 " , // educated guess
false ,
" Maximum number of alarms to remember inside the application. "
) ;
mSchema [ tmp - > getName ( ) ] = * tmp ;
2013-08-13 16:17:46 -07:00
delete tmp ;
2013-05-31 14:47:42 -07:00
tmp = new ConfigurationKey ( " Log.File " , " " ,
" " ,
ConfigurationKey : : DEVELOPER ,
ConfigurationKey : : FILEPATH_OPT , // audited
" " ,
false ,
" Path to use for textfile based logging. "
" By default, this feature is disabled. "
" To enable, specify an absolute path to the file you wish to use, eg: /tmp/my-debug.log. "
" To disable again, execute \" unconfig Log.File \" . "
) ;
mSchema [ tmp - > getName ( ) ] = * tmp ;
2013-08-13 16:17:46 -07:00
delete tmp ;
2013-05-31 14:47:42 -07:00
tmp = new ConfigurationKey ( " Log.Level " , " NOTICE " ,
" " ,
ConfigurationKey : : CUSTOMER ,
ConfigurationKey : : CHOICE ,
" EMERG|EMERGENCY - report serious faults associated with service failure or hardware damage, "
" ALERT|ALERT - report likely service disruption caused by misconfiguration or poor connectivity, "
" CRIT|CRITICAL - report anomalous events that are likely to degrade service, "
" ERR|ERROR - report internal errors of the software that may result in degradation of service in unusual circumstances, "
" WARNING|WARNING - report anomalous events that may indicate a degradation of normal service, "
" NOTICE|NOTICE - report anomalous events that probably do not affect service but may be of interest to network operators, "
" INFO|INFORMATION - report normal events, "
" DEBUG|DEBUG - only for use by developers and will degrade system performance " ,
false ,
" Default logging level when no other level is defined for a file. "
) ;
mSchema [ tmp - > getName ( ) ] = * tmp ;
2013-08-13 16:17:46 -07:00
delete tmp ;
2013-05-31 14:47:42 -07:00
// Add application specific schema
mSchema . insert ( wSchema . begin ( ) , wSchema . end ( ) ) ;
// Init the cross checking callback to something predictable
mCrossCheck = NULL ;
}
string ConfigurationTable : : getDefaultSQL ( const std : : string & program , const std : : string & version )
{
stringstream ss ;
ConfigurationKeyMap : : iterator mp ;
ss < < " -- " < < endl ;
ss < < " -- This file was generated using: " < < program < < " --gensql " < < endl ;
ss < < " -- binary version: " < < version < < endl ;
ss < < " -- " < < endl ;
ss < < " -- Future changes should not be put in this file directly but " < < endl ;
ss < < " -- rather in the program's ConfigurationKey schema. " < < endl ;
ss < < " -- " < < endl ;
ss < < " PRAGMA foreign_keys=OFF; " < < endl ;
2013-08-13 16:17:46 -07:00
ss < < " PRAGMA journal_mode=WAL; " < < endl ;
2013-05-31 14:47:42 -07:00
ss < < " BEGIN TRANSACTION; " < < endl ;
2013-08-13 16:17:46 -07:00
ss < < " CREATE TABLE IF NOT EXISTS CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT ''); " < < endl ;
2013-05-31 14:47:42 -07:00
mp = mSchema . begin ( ) ;
while ( mp ! = mSchema . end ( ) ) {
2013-08-13 16:17:46 -07:00
ss < < " INSERT OR IGNORE INTO \" CONFIG \" VALUES( " ;
2013-05-31 14:47:42 -07:00
// name
ss < < " ' " < < mp - > first < < " ', " ;
// default
ss < < " ' " < < mp - > second . getDefaultValue ( ) < < " ', " ;
// static
if ( mp - > second . isStatic ( ) ) {
ss < < " 1 " ;
} else {
ss < < " 0 " ;
}
ss < < " , " ;
// optional
ss < < " 0, " ;
// description
ss < < " ' " ;
if ( mp - > second . getType ( ) = = ConfigurationKey : : BOOLEAN ) {
ss < < " 1=enabled, 0=disabled - " ;
}
ss < < mp - > second . getDescription ( ) ;
if ( mp - > second . isStatic ( ) ) {
ss < < " Static. " ;
}
ss < < " ' " ;
ss < < " ); " < < endl ;
mp + + ;
2011-10-07 12:20:49 -07:00
}
2013-05-31 14:47:42 -07:00
ss < < " COMMIT; " < < endl ;
ss < < endl ;
return ss . str ( ) ;
2011-10-07 12:20:49 -07:00
}
2013-05-31 14:47:42 -07:00
string ConfigurationTable : : getTeX ( const std : : string & program , const std : : string & version )
{
stringstream ss ;
ConfigurationKeyMap : : iterator mp ;
2011-10-07 12:20:49 -07:00
2013-05-31 14:47:42 -07:00
ss < < " % START AUTO-GENERATED CONTENT " < < endl ;
ss < < " % -- these sections were generated using: " < < program < < " --gentex " < < endl ;
ss < < " % -- binary version: " < < version < < endl ;
ss < < " \\ subsection{Customer Site Parameters} " < < endl ;
ss < < " These parameters must be changed to fit your site. " < < endl ;
ss < < " \\ begin{itemize} " < < endl ;
mp = mSchema . begin ( ) ;
while ( mp ! = mSchema . end ( ) ) {
if ( mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERSITE ) {
ss < < " \\ item " ;
// name
ss < < mp - > first < < " -- " ;
// description
ss < < mp - > second . getDescription ( ) ;
ss < < endl ;
}
mp + + ;
}
ss < < " \\ end{itemize} " < < endl ;
ss < < endl ;
ss < < " \\ subsection{Customer Tuneable Parameters} " < < endl ;
ss < < " These parameters can be changed to optimize your site. " < < endl ;
ss < < " \\ begin{itemize} " < < endl ;
mp = mSchema . begin ( ) ;
while ( mp ! = mSchema . end ( ) ) {
if ( mp - > second . getVisibility ( ) ! = ConfigurationKey : : CUSTOMERSITE & &
(
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMER | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERTUNE | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : CUSTOMERWARN
) ) {
ss < < " \\ item " ;
// name
ss < < mp - > first < < " -- " ;
// description
ss < < mp - > second . getDescription ( ) ;
ss < < endl ;
}
mp + + ;
}
ss < < " \\ end{itemize} " < < endl ;
ss < < endl ;
ss < < " \\ subsection{Developer/Factory Parameters} " < < endl ;
ss < < " These parameters should only be changed by when developing new code. " < < endl ;
ss < < " \\ begin{itemize} " < < endl ;
mp = mSchema . begin ( ) ;
while ( mp ! = mSchema . end ( ) ) {
if ( mp - > second . getVisibility ( ) = = ConfigurationKey : : FACTORY | |
mp - > second . getVisibility ( ) = = ConfigurationKey : : DEVELOPER ) {
ss < < " \\ item " ;
// name
ss < < mp - > first < < " -- " ;
// description
ss < < mp - > second . getDescription ( ) ;
ss < < endl ;
}
mp + + ;
}
ss < < " \\ end{itemize} " < < endl ;
ss < < " % END AUTO-GENERATED CONTENT " < < endl ;
ss < < endl ;
string tmp = Utils : : replaceAll ( ss . str ( ) , " ^ " , " \\ ^ " ) ;
return Utils : : replaceAll ( tmp , " _ " , " \\ _ " ) ;
}
2011-10-07 12:20:49 -07:00
bool ConfigurationTable : : defines ( const string & key )
{
2013-05-31 14:47:42 -07:00
try {
ScopedLock lock ( mLock ) ;
return lookup ( key ) . defined ( ) ;
} catch ( ConfigurationTableKeyNotFound ) {
2013-08-13 16:17:46 -07:00
// TODO: re-enable once we figure out why this message is being sent to syslog regardless of log level
//gLogEarly(LOG_DEBUG, "configuration parameter %s not found", key.c_str());
2013-05-31 14:47:42 -07:00
return false ;
}
}
2011-10-07 12:20:49 -07:00
2013-05-31 14:47:42 -07:00
bool ConfigurationTable : : keyDefinedInSchema ( const std : : string & name )
{
return mSchema . find ( name ) = = mSchema . end ( ) ? false : true ;
}
2011-10-07 12:20:49 -07:00
2013-05-31 14:47:42 -07:00
bool ConfigurationTable : : isValidValue ( const std : : string & name , const std : : string & val ) {
bool ret = false ;
2011-10-07 12:20:49 -07:00
2013-05-31 14:47:42 -07:00
ConfigurationKey key = mSchema [ name ] ;
switch ( key . getType ( ) ) {
case ConfigurationKey : : BOOLEAN : {
if ( val = = " 1 " | | val = = " 0 " ) {
ret = true ;
}
break ;
}
case ConfigurationKey : : CHOICE_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : CHOICE : {
int startPos = - 1 ;
uint endPos = 0 ;
std : : string tmp = key . getValidValues ( ) ;
do {
startPos + + ;
if ( ( endPos = tmp . find ( ' | ' , startPos ) ) ! = std : : string : : npos ) {
if ( val = = tmp . substr ( startPos , endPos - startPos ) ) {
ret = true ;
break ;
}
} else {
if ( val = = tmp . substr ( startPos , tmp . find ( ' , ' , startPos ) - startPos ) ) {
ret = true ;
break ;
}
}
} while ( ( startPos = tmp . find ( ' , ' , startPos ) ) ! = ( int ) std : : string : : npos ) ;
break ;
}
case ConfigurationKey : : CIDR_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : CIDR : {
uint delimiter ;
std : : string ip ;
int cidr = - 1 ;
delimiter = val . find ( ' / ' ) ;
if ( delimiter ! = std : : string : : npos ) {
ip = val . substr ( 0 , delimiter ) ;
std : : stringstream ( val . substr ( delimiter + 1 ) ) > > cidr ;
if ( ConfigurationKey : : isValidIP ( ip ) & & 0 < = cidr & & cidr < = 32 ) {
ret = true ;
}
}
break ;
}
case ConfigurationKey : : FILEPATH_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : FILEPATH : {
regex_t r ;
const char * expression = " ^[a-zA-Z0-9/_.-]+$ " ;
int result = regcomp ( & r , expression , REG_EXTENDED ) ;
if ( result ) {
char msg [ 256 ] ;
regerror ( result , & r , msg , 255 ) ;
break ; //abort();
}
if ( regexec ( & r , val . c_str ( ) , 0 , NULL , 0 ) = = 0 ) {
ret = true ;
}
regfree ( & r ) ;
break ;
}
case ConfigurationKey : : IPADDRESS_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : IPADDRESS : {
ret = ConfigurationKey : : isValidIP ( val ) ;
break ;
}
case ConfigurationKey : : IPANDPORT : {
uint delimiter ;
std : : string ip ;
int port = - 1 ;
delimiter = val . find ( ' : ' ) ;
if ( delimiter ! = std : : string : : npos ) {
ip = val . substr ( 0 , delimiter ) ;
std : : stringstream ( val . substr ( delimiter + 1 ) ) > > port ;
if ( ConfigurationKey : : isValidIP ( ip ) & & 1 < = port & & port < = 65535 ) {
ret = true ;
}
}
break ;
}
case ConfigurationKey : : MIPADDRESS_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : MIPADDRESS : {
int startPos = - 1 ;
uint endPos = 0 ;
do {
startPos + + ;
endPos = val . find ( ' ' , startPos ) ;
if ( ConfigurationKey : : isValidIP ( val . substr ( startPos , endPos - startPos ) ) ) {
ret = true ;
} else {
ret = false ;
break ;
}
} while ( ( startPos = endPos ) ! = ( int ) std : : string : : npos ) ;
break ;
}
case ConfigurationKey : : PORT_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : PORT : {
int intVal ;
std : : stringstream ( val ) > > intVal ;
if ( 1 < = intVal & & intVal < = 65535 ) {
ret = true ;
}
break ;
}
case ConfigurationKey : : REGEX_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : REGEX : {
regex_t r ;
const char * expression = val . c_str ( ) ;
int result = regcomp ( & r , expression , REG_EXTENDED ) ;
if ( result ) {
char msg [ 256 ] ;
regerror ( result , & r , msg , 255 ) ;
} else {
ret = true ;
}
regfree ( & r ) ;
break ;
}
case ConfigurationKey : : STRING_OPT : {
if ( val . length ( ) = = 0 ) {
ret = true ;
break ;
}
}
case ConfigurationKey : : STRING : {
regex_t r ;
const char * expression = key . getValidValues ( ) . c_str ( ) ;
int result = regcomp ( & r , expression , REG_EXTENDED ) ;
if ( result ) {
char msg [ 256 ] ;
regerror ( result , & r , msg , 255 ) ;
break ; //abort();
}
if ( regexec ( & r , val . c_str ( ) , 0 , NULL , 0 ) = = 0 ) {
ret = true ;
}
regfree ( & r ) ;
break ;
}
case ConfigurationKey : : VALRANGE : {
regex_t r ;
int result ;
if ( key . getValidValues ( ) . find ( ' . ' ) ! = std : : string : : npos ) {
result = regcomp ( & r , " ^[0-9.-]+$ " , REG_EXTENDED ) ;
} else {
result = regcomp ( & r , " ^[0-9-]+$ " , REG_EXTENDED ) ;
}
if ( result ) {
char msg [ 256 ] ;
regerror ( result , & r , msg , 255 ) ;
break ; //abort();
}
if ( regexec ( & r , val . c_str ( ) , 0 , NULL , 0 ) ! = 0 ) {
ret = false ;
} else if ( key . getValidValues ( ) . find ( ' . ' ) ! = std : : string : : npos ) {
ret = ConfigurationKey : : isInValRange < float > ( key , val , false ) ;
} else {
ret = ConfigurationKey : : isInValRange < int > ( key , val , true ) ;
}
regfree ( & r ) ;
break ;
}
2011-10-07 12:20:49 -07:00
}
2013-05-31 14:47:42 -07:00
return ret ;
2011-10-07 12:20:49 -07:00
}
2013-05-31 14:47:42 -07:00
ConfigurationKeyMap ConfigurationTable : : getSimilarKeys ( const std : : string & snippet ) {
ConfigurationKeyMap tmp ;
ConfigurationKeyMap : : const_iterator mp = mSchema . begin ( ) ;
while ( mp ! = mSchema . end ( ) ) {
if ( mp - > first . find ( snippet ) ! = std : : string : : npos ) {
tmp [ mp - > first ] = mp - > second ;
}
mp + + ;
}
return tmp ;
}
2011-10-07 12:20:49 -07:00
const ConfigurationRecord & ConfigurationTable : : lookup ( const string & key )
{
assert ( mDB ) ;
checkCacheAge ( ) ;
// We assume the caller holds mLock.
// So it is OK to return a reference into the cache.
// Check the cache.
// This is cheap.
ConfigurationMap : : const_iterator where = mCache . find ( key ) ;
if ( where ! = mCache . end ( ) ) {
if ( where - > second . defined ( ) ) return where - > second ;
throw ConfigurationTableKeyNotFound ( key ) ;
}
// Check the database.
// This is more expensive.
char * value = NULL ;
sqlite3_single_lookup ( mDB , " CONFIG " ,
" KEYSTRING " , key . c_str ( ) , " VALUESTRING " , value ) ;
2013-05-31 14:47:42 -07:00
// value found, cache the result
if ( value ) {
mCache [ key ] = ConfigurationRecord ( value ) ;
// key definition found, cache the default
} else if ( keyDefinedInSchema ( key ) ) {
mCache [ key ] = ConfigurationRecord ( mSchema [ key ] . getDefaultValue ( ) ) ;
// total miss, cache the error
} else {
2011-10-07 12:20:49 -07:00
mCache [ key ] = ConfigurationRecord ( false ) ;
throw ConfigurationTableKeyNotFound ( key ) ;
}
free ( value ) ;
// Leave mLock locked. The caller holds it still.
return mCache [ key ] ;
}
2013-05-31 14:47:42 -07:00
bool ConfigurationTable : : isStatic ( const string & key )
2011-10-07 12:20:49 -07:00
{
2013-05-31 14:47:42 -07:00
if ( keyDefinedInSchema ( key ) ) {
return mSchema [ key ] . isStatic ( ) ;
} else {
return false ;
}
2011-10-07 12:20:49 -07:00
}
string ConfigurationTable : : getStr ( const string & key )
{
// We need the lock because rec is a reference into the cache.
2013-05-31 14:47:42 -07:00
try {
ScopedLock lock ( mLock ) ;
return lookup ( key ) . value ( ) ;
} catch ( ConfigurationTableKeyNotFound ) {
// Raise an alert and re-throw the exception.
2013-08-13 16:17:46 -07:00
gLogEarly ( LOG_DEBUG , " configuration parameter %s has no defined value " , key . c_str ( ) ) ;
2013-05-31 14:47:42 -07:00
throw ConfigurationTableKeyNotFound ( key ) ;
}
2011-10-07 12:20:49 -07:00
}
2013-05-31 14:47:42 -07:00
bool ConfigurationTable : : getBool ( const string & key )
2011-10-07 12:20:49 -07:00
{
try {
2013-05-31 14:47:42 -07:00
return getNum ( key ) ! = 0 ;
2011-10-07 12:20:49 -07:00
} catch ( ConfigurationTableKeyNotFound ) {
2013-05-31 14:47:42 -07:00
// Raise an alert and re-throw the exception.
2013-08-13 16:17:46 -07:00
gLogEarly ( LOG_DEBUG , " configuration parameter %s has no defined value " , key . c_str ( ) ) ;
2013-05-31 14:47:42 -07:00
throw ConfigurationTableKeyNotFound ( key ) ;
2011-10-07 12:20:49 -07:00
}
}
long ConfigurationTable : : getNum ( const string & key )
{
// We need the lock because rec is a reference into the cache.
2013-05-31 14:47:42 -07:00
try {
ScopedLock lock ( mLock ) ;
return lookup ( key ) . number ( ) ;
} catch ( ConfigurationTableKeyNotFound ) {
// Raise an alert and re-throw the exception.
2013-08-13 16:17:46 -07:00
gLogEarly ( LOG_DEBUG , " configuration parameter %s has no defined value " , key . c_str ( ) ) ;
2013-05-31 14:47:42 -07:00
throw ConfigurationTableKeyNotFound ( key ) ;
}
2011-10-07 12:20:49 -07:00
}
2013-05-31 14:47:42 -07:00
float ConfigurationTable : : getFloat ( const string & key )
2011-10-07 12:20:49 -07:00
{
try {
2013-05-31 14:47:42 -07:00
ScopedLock lock ( mLock ) ;
return lookup ( key ) . floatNumber ( ) ;
2011-10-07 12:20:49 -07:00
} catch ( ConfigurationTableKeyNotFound ) {
2013-05-31 14:47:42 -07:00
// Raise an alert and re-throw the exception.
2013-08-13 16:17:46 -07:00
gLogEarly ( LOG_DEBUG , " configuration parameter %s has no defined value " , key . c_str ( ) ) ;
2013-05-31 14:47:42 -07:00
throw ConfigurationTableKeyNotFound ( key ) ;
2011-10-07 12:20:49 -07:00
}
}
2013-05-31 14:47:42 -07:00
std : : vector < string > ConfigurationTable : : getVectorOfStrings ( const string & key )
{
// Look up the string.
char * line = NULL ;
try {
ScopedLock lock ( mLock ) ;
const ConfigurationRecord & rec = lookup ( key ) ;
line = strdup ( rec . value ( ) . c_str ( ) ) ;
} catch ( ConfigurationTableKeyNotFound ) {
// Raise an alert and re-throw the exception.
2013-08-13 16:17:46 -07:00
gLogEarly ( LOG_DEBUG , " configuration parameter %s has no defined value " , key . c_str ( ) ) ;
2013-05-31 14:47:42 -07:00
throw ConfigurationTableKeyNotFound ( key ) ;
}
assert ( line ) ;
char * lp = line ;
// Parse the string.
std : : vector < string > retVal ;
while ( lp ) {
while ( * lp = = ' ' ) lp + + ;
if ( * lp = = ' \0 ' ) break ;
char * tp = strsep ( & lp , " " ) ;
if ( ! tp ) break ;
retVal . push_back ( tp ) ;
}
free ( line ) ;
return retVal ;
}
2011-10-07 12:20:49 -07:00
std : : vector < unsigned > ConfigurationTable : : getVector ( const string & key )
{
// Look up the string.
2013-05-31 14:47:42 -07:00
char * line = NULL ;
try {
ScopedLock lock ( mLock ) ;
const ConfigurationRecord & rec = lookup ( key ) ;
line = strdup ( rec . value ( ) . c_str ( ) ) ;
} catch ( ConfigurationTableKeyNotFound ) {
// Raise an alert and re-throw the exception.
2013-08-13 16:17:46 -07:00
gLogEarly ( LOG_DEBUG , " configuration parameter %s has no defined value " , key . c_str ( ) ) ;
2013-05-31 14:47:42 -07:00
throw ConfigurationTableKeyNotFound ( key ) ;
}
assert ( line ) ;
char * lp = line ;
2011-10-07 12:20:49 -07:00
// Parse the string.
std : : vector < unsigned > retVal ;
while ( lp ) {
// Watch for multiple or trailing spaces.
while ( * lp = = ' ' ) lp + + ;
if ( * lp = = ' \0 ' ) break ;
retVal . push_back ( strtol ( lp , NULL , 0 ) ) ;
strsep ( & lp , " " ) ;
}
free ( line ) ;
return retVal ;
}
2013-05-31 14:47:42 -07:00
bool ConfigurationTable : : remove ( const string & key )
2011-10-07 12:20:49 -07:00
{
assert ( mDB ) ;
ScopedLock lock ( mLock ) ;
// Clear the cache entry and the database.
ConfigurationMap : : iterator where = mCache . find ( key ) ;
if ( where ! = mCache . end ( ) ) mCache . erase ( where ) ;
2013-05-31 14:47:42 -07:00
// Really remove it.
string cmd = " DELETE FROM CONFIG WHERE KEYSTRING==' " + key + " ' " ;
2011-10-07 12:20:49 -07:00
return sqlite3_command ( mDB , cmd . c_str ( ) ) ;
}
2013-05-31 14:47:42 -07:00
2011-10-07 12:20:49 -07:00
void ConfigurationTable : : find ( const string & pat , ostream & os ) const
{
// Prepare the statement.
string cmd = " SELECT KEYSTRING,VALUESTRING FROM CONFIG WHERE KEYSTRING LIKE \" % " + pat + " % \" " ;
sqlite3_stmt * stmt ;
if ( sqlite3_prepare_statement ( mDB , & stmt , cmd . c_str ( ) ) ) return ;
// Read the result.
int src = sqlite3_run_query ( mDB , stmt ) ;
while ( src = = SQLITE_ROW ) {
const char * value = ( const char * ) sqlite3_column_text ( stmt , 1 ) ;
os < < sqlite3_column_text ( stmt , 0 ) < < " " ;
2013-05-31 14:47:42 -07:00
int len = 0 ;
if ( value ) {
len = strlen ( value ) ;
}
if ( len & & value ) os < < value < < endl ;
else os < < " (disabled) " < < endl ;
2011-10-07 12:20:49 -07:00
src = sqlite3_run_query ( mDB , stmt ) ;
}
sqlite3_finalize ( stmt ) ;
}
2013-05-31 14:47:42 -07:00
ConfigurationRecordMap ConfigurationTable : : getAllPairs ( ) const
{
ConfigurationRecordMap tmp ;
// Prepare the statement.
string cmd = " SELECT KEYSTRING,VALUESTRING FROM CONFIG " ;
sqlite3_stmt * stmt ;
if ( sqlite3_prepare_statement ( mDB , & stmt , cmd . c_str ( ) ) ) return tmp ;
// Read the result.
int src = sqlite3_run_query ( mDB , stmt ) ;
while ( src = = SQLITE_ROW ) {
const char * key = ( const char * ) sqlite3_column_text ( stmt , 0 ) ;
const char * value = ( const char * ) sqlite3_column_text ( stmt , 1 ) ;
if ( key & & value ) {
tmp [ string ( key ) ] = ConfigurationRecord ( value ) ;
} else if ( key & & ! value ) {
tmp [ string ( key ) ] = ConfigurationRecord ( false ) ;
}
src = sqlite3_run_query ( mDB , stmt ) ;
}
sqlite3_finalize ( stmt ) ;
return tmp ;
}
2011-10-07 12:20:49 -07:00
bool ConfigurationTable : : set ( const string & key , const string & value )
{
assert ( mDB ) ;
ScopedLock lock ( mLock ) ;
2013-08-13 16:17:46 -07:00
string cmd ;
if ( keyDefinedInSchema ( key ) ) {
cmd = " INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL,COMMENTS) VALUES ( \" " + key + " \" , \" " + value + " \" ,1, \' " + mSchema [ key ] . getDescription ( ) + " \' ) " ;
} else {
cmd = " INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES ( \" " + key + " \" , \" " + value + " \" ,1) " ;
}
2011-10-07 12:20:49 -07:00
bool success = sqlite3_command ( mDB , cmd . c_str ( ) ) ;
// Cache the result.
if ( success ) mCache [ key ] = ConfigurationRecord ( value ) ;
return success ;
}
bool ConfigurationTable : : set ( const string & key , long value )
{
char buffer [ 30 ] ;
sprintf ( buffer , " %ld " , value ) ;
return set ( key , buffer ) ;
}
void ConfigurationTable : : checkCacheAge ( )
{
// mLock is set by caller
static time_t timeOfLastPurge = 0 ;
time_t now = time ( NULL ) ;
// purge every 3 seconds
// purge period cannot be configuration parameter
if ( now - timeOfLastPurge < 3 ) return ;
timeOfLastPurge = now ;
// this is purge() without the lock
ConfigurationMap : : iterator mp = mCache . begin ( ) ;
while ( mp ! = mCache . end ( ) ) {
ConfigurationMap : : iterator prev = mp ;
mp + + ;
mCache . erase ( prev ) ;
}
}
void ConfigurationTable : : purge ( )
{
ScopedLock lock ( mLock ) ;
ConfigurationMap : : iterator mp = mCache . begin ( ) ;
while ( mp ! = mCache . end ( ) ) {
ConfigurationMap : : iterator prev = mp ;
mp + + ;
mCache . erase ( prev ) ;
}
}
void ConfigurationTable : : setUpdateHook ( void ( * func ) ( void * , int , char const * , char const * , sqlite3_int64 ) )
{
assert ( mDB ) ;
sqlite3_update_hook ( mDB , func , NULL ) ;
}
2013-05-31 14:47:42 -07:00
void ConfigurationTable : : setCrossCheckHook ( vector < string > ( * wCrossCheck ) ( const string & ) )
{
mCrossCheck = wCrossCheck ;
}
vector < string > ConfigurationTable : : crossCheck ( const string & key ) {
vector < string > ret ;
if ( mCrossCheck ! = NULL ) {
ret = mCrossCheck ( key ) ;
}
return ret ;
}
2011-10-07 12:20:49 -07:00
void HashString : : computeHash ( )
{
// FIXME -- Someone needs to review this hash function.
const char * cstr = c_str ( ) ;
mHash = 0 ;
for ( unsigned i = 0 ; i < size ( ) ; i + + ) {
mHash = mHash ^ ( mHash > > 32 ) ;
mHash = mHash * 127 + cstr [ i ] ;
}
}
2013-05-31 14:47:42 -07:00
void SimpleKeyValue : : addItem ( const char * pair_orig )
{
char * pair = strdup ( pair_orig ) ;
char * key = pair ;
char * mark = strchr ( pair , ' = ' ) ;
if ( ! mark ) return ;
* mark = ' \0 ' ;
char * value = mark + 1 ;
mMap [ key ] = value ;
free ( pair ) ;
}
const char * SimpleKeyValue : : get ( const char * key ) const
{
HashStringMap : : const_iterator p = mMap . find ( key ) ;
if ( p = = mMap . end ( ) ) return NULL ;
return p - > second . c_str ( ) ;
}
void SimpleKeyValue : : addItems ( const char * pairs_orig )
{
char * pairs = strdup ( pairs_orig ) ;
char * thisPair ;
while ( ( thisPair = strsep ( & pairs , " " ) ) ! = NULL ) {
addItem ( thisPair ) ;
}
free ( pairs ) ;
}
bool ConfigurationKey : : isValidIP ( const std : : string & ip ) {
struct sockaddr_in sa ;
return inet_pton ( AF_INET , ip . c_str ( ) , & ( sa . sin_addr ) ) ! = 0 ;
}
void ConfigurationKey : : getMinMaxStepping ( const ConfigurationKey & key , std : : string & min , std : : string & max , std : : string & stepping ) {
uint delimiter ;
int startPos ;
uint endPos ;
std : : string tmp = key . getValidValues ( ) ;
stepping = " 1 " ;
// grab steps if they're defined
startPos = tmp . find ( ' ( ' ) ;
if ( startPos ! = ( int ) std : : string : : npos ) {
endPos = tmp . find ( ' ) ' ) ;
stepping = tmp . substr ( startPos + 1 , endPos - startPos - 1 ) ;
tmp = tmp . substr ( 0 , startPos ) ;
}
startPos = 0 ;
delimiter = tmp . find ( ' : ' , startPos ) ;
min = tmp . substr ( startPos , delimiter - startPos ) ;
max = tmp . substr ( delimiter + 1 , tmp . find ( ' , ' , delimiter ) - delimiter - 1 ) ;
}
template < class T > bool ConfigurationKey : : isInValRange ( const ConfigurationKey & key , const std : : string & val , const bool isInteger ) {
bool ret = false ;
T convVal ;
T min ;
T max ;
T steps ;
std : : string strMin ;
std : : string strMax ;
std : : string strSteps ;
std : : stringstream ( val ) > > convVal ;
ConfigurationKey : : getMinMaxStepping ( key , strMin , strMax , strSteps ) ;
std : : stringstream ( strMin ) > > min ;
std : : stringstream ( strMax ) > > max ;
std : : stringstream ( strSteps ) > > steps ;
// TODO : only ranges checked, steps not enforced
if ( isInteger ) {
if ( val . find ( ' . ' ) = = std : : string : : npos & & min < = convVal & & convVal < = max ) {
ret = true ;
}
} else {
if ( min < = convVal & & convVal < = max ) {
ret = true ;
}
}
return ret ;
}
const std : : string ConfigurationKey : : getARFCNsString ( ) {
stringstream ss ;
int i ;
float downlink ;
float uplink ;
// 128:251 GSM850
downlink = 869.2 ;
uplink = 824.2 ;
for ( i = 128 ; i < = 251 ; i + + ) {
ss < < i < < " |GSM850 # " < < i < < " : " < < downlink < < " MHz downlink / " < < uplink < < " MHz uplink, " ;
downlink + = 0.2 ;
uplink + = 0.2 ;
}
2013-12-31 11:55:16 -08:00
// 0:124 EGSM900
downlink = 935.0 ;
uplink = 890.0 ;
for ( i = 0 ; i < = 124 ; i + + ) {
ss < < i < < " |PGSM900 # " < < i < < " : " < < ( downlink + ( 0.2 * i ) ) < < " MHz downlink / " < < ( uplink + ( 0.2 * i ) ) < < " MHz uplink, " ;
}
// 975:1023 EGSM900
for ( i = 975 ; i < = 1023 ; i + + ) {
ss < < i < < " |PGSM900 # " < < i < < " : " < < ( downlink + ( 0.2 * i ) ) < < " MHz downlink / " < < ( uplink + ( 0.2 * i ) ) < < " MHz uplink, " ;
2013-05-31 14:47:42 -07:00
}
// 512:885 DCS1800
downlink = 1805.2 ;
uplink = 1710.2 ;
for ( i = 512 ; i < = 885 ; i + + ) {
ss < < i < < " |DCS1800 # " < < i < < " : " < < downlink < < " MHz downlink / " < < uplink < < " MHz uplink, " ;
downlink + = 0.2 ;
uplink + = 0.2 ;
}
// 512:810 PCS1900
downlink = 1930.2 ;
uplink = 1850.2 ;
for ( i = 512 ; i < = 810 ; i + + ) {
ss < < i < < " |PCS1900 # " < < i < < " : " < < downlink < < " MHz downlink / " < < uplink < < " MHz uplink, " ;
downlink + = 0.2 ;
uplink + = 0.2 ;
}
ss < < endl ;
return ss . str ( ) ;
}
const std : : string ConfigurationKey : : visibilityLevelToString ( const ConfigurationKey : : VisibilityLevel & visibility ) {
std : : string ret = " UNKNOWN ERROR " ;
switch ( visibility ) {
case ConfigurationKey : : CUSTOMER :
ret = " customer - can be freely changed by the customer without any detriment to their system " ;
break ;
case ConfigurationKey : : CUSTOMERSITE :
ret = " customer site - these values are different for each BTS and should not be left default " ;
break ;
case ConfigurationKey : : CUSTOMERTUNE :
ret = " customer tune - should only be changed to tune an installation to better suit the physical environment or MS usage pattern " ;
break ;
case ConfigurationKey : : CUSTOMERWARN :
ret = " customer warn - a warning will be presented and confirmation required before changing this sensitive setting " ;
break ;
case ConfigurationKey : : DEVELOPER :
ret = " developer - should only be changed by developers to debug/optimize the implementation " ;
break ;
case ConfigurationKey : : FACTORY :
ret = " factory - set once at the factory, should never be changed " ;
break ;
}
return ret ;
}
const std : : string ConfigurationKey : : typeToString ( const ConfigurationKey : : Type & type ) {
std : : string ret = " UNKNOWN ERROR " ;
switch ( type ) {
case BOOLEAN :
ret = " boolean " ;
break ;
case CHOICE_OPT :
ret = " multiple choice (optional) " ;
break ;
case CHOICE :
ret = " multiple choice " ;
break ;
case CIDR_OPT :
ret = " CIDR notation (optional) " ;
break ;
case CIDR :
ret = " CIDR notation " ;
break ;
case FILEPATH_OPT :
ret = " file path (optional) " ;
break ;
case FILEPATH :
ret = " file path " ;
break ;
case IPADDRESS_OPT :
ret = " IP address (optional) " ;
break ;
case IPADDRESS :
ret = " IP address " ;
break ;
case IPANDPORT :
ret = " IP address and port " ;
break ;
case MIPADDRESS_OPT :
ret = " space-separated list of IP addresses (optional) " ;
break ;
case MIPADDRESS :
ret = " space-separated list of IP addresses " ;
break ;
case PORT_OPT :
ret = " IP port (optional) " ;
break ;
case PORT :
ret = " IP port " ;
break ;
case REGEX_OPT :
ret = " regular expression (optional) " ;
break ;
case REGEX :
ret = " regular expression " ;
break ;
case STRING_OPT :
ret = " string (optional) " ;
break ;
case STRING :
ret = " string " ;
break ;
case VALRANGE :
ret = " value range " ;
break ;
}
return ret ;
}
void ConfigurationKey : : printKey ( const ConfigurationKey & key , const std : : string & currentValue , ostream & os ) {
os < < key . getName ( ) < < " " ;
if ( ! currentValue . length ( ) ) {
os < < " (disabled) " ;
} else {
os < < currentValue ;
}
if ( currentValue . compare ( key . getDefaultValue ( ) ) = = 0 ) {
os < < " [default] " ;
}
os < < endl ;
}
void ConfigurationKey : : printDescription ( const ConfigurationKey & key , ostream & os ) {
std : : string tmp ;
os < < " - description: " < < key . getDescription ( ) < < std : : endl ;
if ( key . getUnits ( ) . length ( ) ) {
os < < " - units: " < < key . getUnits ( ) < < std : : endl ;
}
os < < " - type: " < < ConfigurationKey : : typeToString ( key . getType ( ) ) < < std : : endl ;
if ( key . getDefaultValue ( ) . length ( ) ) {
os < < " - default value: " < < key . getDefaultValue ( ) < < std : : endl ;
}
os < < " - visibility level: " < < ConfigurationKey : : visibilityLevelToString ( key . getVisibility ( ) ) < < std : : endl ;
os < < " - static: " < < key . isStatic ( ) < < std : : endl ;
tmp = key . getValidValues ( ) ;
if ( key . getType ( ) = = ConfigurationKey : : VALRANGE ) {
int startPos = tmp . find ( ' ( ' ) ;
uint delimiter = 0 ;
if ( startPos ! = ( int ) std : : string : : npos ) {
tmp = tmp . substr ( 0 , startPos ) ;
}
startPos = - 1 ;
do {
startPos + + ;
delimiter = tmp . find ( ' : ' , startPos ) ;
os < < " - valid values: " < < " from " < < tmp . substr ( startPos , delimiter - startPos ) < < " to "
< < tmp . substr ( delimiter + 1 , tmp . find ( ' , ' , delimiter ) - delimiter - 1 ) < < std : : endl ;
} while ( ( startPos = tmp . find ( ' , ' , startPos ) ) ! = ( int ) std : : string : : npos ) ;
} else if ( key . getType ( ) = = ConfigurationKey : : CHOICE ) {
int startPos = - 1 ;
uint endPos = 0 ;
do {
startPos + + ;
if ( ( endPos = tmp . find ( ' | ' , startPos ) ) ! = std : : string : : npos ) {
os < < " - valid values: " < < tmp . substr ( startPos , endPos - startPos ) ;
os < < " = " < < tmp . substr ( endPos + 1 , tmp . find ( ' , ' , endPos ) - endPos - 1 ) < < std : : endl ;
} else {
os < < " - valid values: " < < tmp . substr ( startPos , tmp . find ( ' , ' , startPos ) - startPos ) < < std : : endl ;
}
} while ( ( startPos = tmp . find ( ' , ' , startPos ) ) ! = ( int ) std : : string : : npos ) ;
} else if ( key . getType ( ) = = ConfigurationKey : : BOOLEAN ) {
os < < " - valid values: 0 = disabled " < < std : : endl ;
os < < " - valid values: 1 = enabled " < < std : : endl ;
} else if ( key . getType ( ) = = ConfigurationKey : : STRING ) {
os < < " - valid val regex: " < < tmp < < std : : endl ;
} else if ( key . getValidValues ( ) . length ( ) ) {
os < < " - raw valid values: " < < tmp < < std : : endl ;
}
}
2011-10-07 12:20:49 -07:00
// vim: ts=4 sw=4