From 82c46ff7aeb793d70f11fcaf22b2813b7dc0f78b Mon Sep 17 00:00:00 2001 From: dburgess Date: Fri, 7 Oct 2011 02:40:51 +0000 Subject: Putting the actual OpenBTS P2.8 source code into the public SVN branch. git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2242 19bc5d8c-e614-43d4-8b26-e1612bc8e597 --- CommonLibs/Configuration.cpp | 339 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 CommonLibs/Configuration.cpp (limited to 'CommonLibs/Configuration.cpp') diff --git a/CommonLibs/Configuration.cpp b/CommonLibs/Configuration.cpp new file mode 100644 index 0000000..3ad4f01 --- /dev/null +++ b/CommonLibs/Configuration.cpp @@ -0,0 +1,339 @@ +/* +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. +* Copyright 2010 Kestrel Signal Processing, Inc. +* +* +* 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 . + +*/ + + +#include "Configuration.h" +#include +#include +#include +#include + +using namespace std; + + +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 ''" + ")" +}; + + +ConfigurationTable::ConfigurationTable(const char* filename) +{ + // Connect to the database. + int rc = sqlite3_open(filename,&mDB); + if (rc) { + cerr << "Cannot open configuration database: " << sqlite3_errmsg(mDB); + sqlite3_close(mDB); + mDB = NULL; + return; + } + // Create the table, if needed. + if (!sqlite3_command(mDB,createConfigTable)) { + cerr << "Cannot create configuration table:" << sqlite3_errmsg(mDB); + } +} + + + +bool ConfigurationTable::defines(const string& key) +{ + assert(mDB); + ScopedLock lock(mLock); + + // Check the cache. + checkCacheAge(); + ConfigurationMap::const_iterator where = mCache.find(key); + if (where!=mCache.end()) return where->second.defined(); + + // Check the database. + char *value = NULL; + sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",value); + + // Cache the result. + if (value) { + mCache[key] = ConfigurationRecord(value); + free(value); + return true; + } + + mCache[key] = ConfigurationRecord(false); + return false; +} + + +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; + // Unlock the mutex before throwing the exception. + mLock.unlock(); + syslog(LOG_ALERT, "configuration key %s not found", key.c_str()); + throw ConfigurationTableKeyNotFound(key); + } + + // Check the database. + // This is more expensive. + char *value = NULL; + sqlite3_single_lookup(mDB,"CONFIG", + "KEYSTRING",key.c_str(),"VALUESTRING",value); + + // Nothing defined? + if (!value) { + // Cache the failure. + mCache[key] = ConfigurationRecord(false); + // Unlock the mutex before throwing the exception. + mLock.unlock(); + throw ConfigurationTableKeyNotFound(key); + } + + // Cache the result. + mCache[key] = ConfigurationRecord(value); + free(value); + + // Leave mLock locked. The caller holds it still. + return mCache[key]; +} + + + +bool ConfigurationTable::isStatic(const string& key) const +{ + assert(mDB); + unsigned stat; + bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"STATIC",stat); + if (success) return (bool)stat; + return false; +} + +bool ConfigurationTable::isRequired(const string& key) const +{ + assert(mDB); + unsigned optional; + bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"OPTIONAL",optional); + if (success) return !((bool)optional); + return false; +} + + + + +string ConfigurationTable::getStr(const string& key) +{ + // We need the lock because rec is a reference into the cache. + ScopedLock lock(mLock); + return lookup(key).value(); +} + +string ConfigurationTable::getStr(const string& key, const char* defaultValue) +{ + try { + return getStr(key); + } catch (ConfigurationTableKeyNotFound) { + set(key,defaultValue); + return string(defaultValue); + } +} + + +long ConfigurationTable::getNum(const string& key) +{ + // We need the lock because rec is a reference into the cache. + ScopedLock lock(mLock); + return lookup(key).number(); +} + + +long ConfigurationTable::getNum(const string& key, long defaultValue) +{ + try { + return getNum(key); + } catch (ConfigurationTableKeyNotFound) { + set(key,defaultValue); + return defaultValue; + } +} + + + +std::vector ConfigurationTable::getVector(const string& key) +{ + // Look up the string. + mLock.lock(); + const ConfigurationRecord& rec = lookup(key); + char* line = strdup(rec.value().c_str()); + mLock.unlock(); + // Parse the string. + std::vector retVal; + char *lp=line; + 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; +} + + +bool ConfigurationTable::unset(const string& key) +{ + assert(mDB); + if (!defines(key)) return true; + if (isRequired(key)) return false; + + ScopedLock lock(mLock); + // Clear the cache entry and the database. + ConfigurationMap::iterator where = mCache.find(key); + if (where!=mCache.end()) mCache.erase(where); + // Don't delete it; just set VALUESTRING to NULL. + string cmd = "UPDATE CONFIG SET VALUESTRING=NULL WHERE KEYSTRING=='"+key+"'"; + return sqlite3_command(mDB,cmd.c_str()); +} + + +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) << " "; + if (value) os << value << endl; + else os << "(null)" << endl; + src = sqlite3_run_query(mDB,stmt); + } + sqlite3_finalize(stmt); +} + + +bool ConfigurationTable::set(const string& key, const string& value) +{ + assert(mDB); + ScopedLock lock(mLock); + // Is it there already? + char * oldValue = NULL; + bool exists = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",oldValue); + // Update or insert as appropriate. + string cmd; + if (exists) cmd = "UPDATE CONFIG SET VALUESTRING=\""+value+"\" WHERE KEYSTRING==\""+key+"\""; + else cmd = "INSERT INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)"; + 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); +} + + +bool ConfigurationTable::set(const string& key) +{ + assert(mDB); + ScopedLock lock(mLock); + string cmd = "INSERT INTO CONFIG (KEYSTRING) VALUES (\"" + key + "\")"; + bool success = sqlite3_command(mDB,cmd.c_str()); + if (success) mCache[key] = ConfigurationRecord(true); + return success; +} + + +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); +} + + + +void HashString::computeHash() +{ + // FIXME -- Someone needs to review this hash function. + const char* cstr = c_str(); + mHash = 0; + for (unsigned i=0; i> 32); + mHash = mHash*127 + cstr[i]; + } +} + + + +// vim: ts=4 sw=4 -- cgit v1.2.3