From bcf60a8fc97afd4a028e7d588a62a3edb5bf0cf8 Mon Sep 17 00:00:00 2001 From: "kurtis.heimerl" Date: Fri, 26 Oct 2012 06:25:56 +0000 Subject: [PATCH] merged private Config to public: r4211: Changes to ConfigurationTable class. Cleaner locking operations, fewer messages for normal operations, ALERT logging for truly abnormal operations. git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@4348 19bc5d8c-e614-43d4-8b26-e1612bc8e597 --- CommonLibs/Configuration.cpp | 193 ++++++++++++++++++++++++++----- CommonLibs/Configuration.h | 70 +++++++++-- CommonLibs/ConfigurationTest.cpp | 40 ++++++- 3 files changed, 267 insertions(+), 36 deletions(-) diff --git a/CommonLibs/Configuration.cpp b/CommonLibs/Configuration.cpp index 3ad4f010..7d0bea2b 100644 --- a/CommonLibs/Configuration.cpp +++ b/CommonLibs/Configuration.cpp @@ -1,6 +1,7 @@ /* * Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * Copyright 2010 Kestrel Signal Processing, Inc. +* Copyright 2011, 2012 Range Networks, Inc. * * * This software is distributed under the terms of the GNU Affero Public License. @@ -33,6 +34,7 @@ using namespace std; +char gCmdName[20] = {0}; // Use a char* to avoid avoid static initialization of string, and race at startup. static const char* createConfigTable = { "CREATE TABLE IF NOT EXISTS CONFIG (" @@ -45,19 +47,36 @@ static const char* createConfigTable = { }; -ConfigurationTable::ConfigurationTable(const char* filename) + +float ConfigurationRecord::floatNumber() const { + float val; + sscanf(mValue.c_str(),"%f",&val); + return val; +} + + +ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName, int wFacility) + :mFacility(wFacility) +{ + syslog(LOG_INFO | mFacility, "opening configuration table from path %s", filename); // Connect to the database. int rc = sqlite3_open(filename,&mDB); + // (pat) When I used malloc here, sqlite3 sporadically crashes. + if (wCmdName) { + strncpy(gCmdName,wCmdName,18); + gCmdName[18] = 0; + strcat(gCmdName,":"); + } if (rc) { - cerr << "Cannot open configuration database: " << sqlite3_errmsg(mDB); + syslog(LOG_EMERG | mFacility, "cannot open configuration database at %s, error message: %s", filename, 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); + syslog(LOG_EMERG | mFacility, "cannot create configuration table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB)); } } @@ -101,9 +120,6 @@ const ConfigurationRecord& ConfigurationTable::lookup(const string& key) 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); } @@ -117,8 +133,6 @@ const ConfigurationRecord& ConfigurationTable::lookup(const string& key) if (!value) { // Cache the failure. mCache[key] = ConfigurationRecord(false); - // Unlock the mutex before throwing the exception. - mLock.unlock(); throw ConfigurationTableKeyNotFound(key); } @@ -156,51 +170,134 @@ bool ConfigurationTable::isRequired(const string& key) const 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(); + try { + ScopedLock lock(mLock); + return lookup(key).value(); + } catch (ConfigurationTableKeyNotFound) { + // Raise an alert and re-throw the exception. + syslog(LOG_ALERT | mFacility, "configuration parameter %s has no defined value", key.c_str()); + throw ConfigurationTableKeyNotFound(key); + } } string ConfigurationTable::getStr(const string& key, const char* defaultValue) { try { - return getStr(key); + ScopedLock lock(mLock); + return lookup(key).value(); } catch (ConfigurationTableKeyNotFound) { + syslog(LOG_NOTICE | mFacility, "deinfing missing parameter %s with value %s", key.c_str(),defaultValue); set(key,defaultValue); return string(defaultValue); } } +bool ConfigurationTable::getBool(const string& key) +{ + try { + return getNum(key) != 0; + } catch (ConfigurationTableKeyNotFound) { + return false; + } +} + + 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(); + try { + ScopedLock lock(mLock); + return lookup(key).number(); + } catch (ConfigurationTableKeyNotFound) { + // Raise an alert and re-throw the exception. + syslog(LOG_ALERT | mFacility, "configuration parameter %s has no defined value", key.c_str()); + throw ConfigurationTableKeyNotFound(key); + } } long ConfigurationTable::getNum(const string& key, long defaultValue) { try { - return getNum(key); + ScopedLock lock(mLock); + return lookup(key).number(); } catch (ConfigurationTableKeyNotFound) { + syslog(LOG_NOTICE | mFacility, "deinfing missing parameter %s with value %ld", key.c_str(),defaultValue); set(key,defaultValue); return defaultValue; } } +float ConfigurationTable::getFloat(const string& key) +{ + // We need the lock because rec is a reference into the cache. + ScopedLock lock(mLock); + return lookup(key).floatNumber(); +} + +std::vector 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. + syslog(LOG_ALERT | mFacility, "configuration parameter %s has no defined value", key.c_str()); + throw ConfigurationTableKeyNotFound(key); + } + + assert(line); + char *lp = line; + + // Parse the string. + std::vector 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; +} + + +std::vector ConfigurationTable::getVectorOfStrings(const string& key, const char* defaultValue){ + try { + return getVectorOfStrings(key); + } catch (ConfigurationTableKeyNotFound) { + set(key,defaultValue); + return getVectorOfStrings(key); + } +} + + 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(); + 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. + syslog(LOG_ALERT | mFacility, "configuration parameter %s has no defined value", key.c_str()); + throw ConfigurationTableKeyNotFound(key); + } + + assert(line); + char *lp = line; + // Parse the string. std::vector retVal; - char *lp=line; while (lp) { // Watch for multiple or trailing spaces. while (*lp==' ') lp++; @@ -228,6 +325,21 @@ bool ConfigurationTable::unset(const string& key) return sqlite3_command(mDB,cmd.c_str()); } +bool ConfigurationTable::remove(const string& key) +{ + assert(mDB); + 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); + // Really remove it. + string cmd = "DELETE FROM CONFIG WHERE KEYSTRING=='"+key+"'"; + return sqlite3_command(mDB,cmd.c_str()); +} + + void ConfigurationTable::find(const string& pat, ostream& os) const { @@ -252,13 +364,7 @@ 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)"; + string cmd = "INSERT OR REPLACE 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); @@ -277,7 +383,7 @@ bool ConfigurationTable::set(const string& key) { assert(mDB); ScopedLock lock(mLock); - string cmd = "INSERT INTO CONFIG (KEYSTRING) VALUES (\"" + key + "\")"; + string cmd = "INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",NULL,1)"; bool success = sqlite3_command(mDB,cmd.c_str()); if (success) mCache[key] = ConfigurationRecord(true); return success; @@ -335,5 +441,38 @@ void HashString::computeHash() } +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); +} + + // vim: ts=4 sw=4 diff --git a/CommonLibs/Configuration.h b/CommonLibs/Configuration.h index c9c4cf3c..0b14633f 100644 --- a/CommonLibs/Configuration.h +++ b/CommonLibs/Configuration.h @@ -1,6 +1,6 @@ /* * Copyright 2009, 2010 Free Software Foundation, Inc. -* Copyright 2010 Kestrel Signal Processing, Inc. +* Copyright 2010, 2012 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. @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -44,6 +45,7 @@ /** A class for configuration file errors. */ class ConfigurationTableError {}; +extern char gCmdName[]; // Gotta be global, gotta be char*, gotta love it. /** An exception thrown when a given config key isn't found. */ class ConfigurationTableKeyNotFound : public ConfigurationTableError { @@ -56,7 +58,7 @@ class ConfigurationTableKeyNotFound : public ConfigurationTableError { ConfigurationTableKeyNotFound(const std::string& wKey) :mKey(wKey) - { std::cerr << "cannot find configuration value " << mKey << std::endl; } + { } const std::string& key() const { return mKey; } @@ -94,6 +96,8 @@ class ConfigurationRecord { long number() const { return mNumber; } bool defined() const { return mDefined; } + float floatNumber() const; + }; @@ -176,11 +180,12 @@ class ConfigurationTable { sqlite3* mDB; ///< database connection ConfigurationMap mCache; ///< cache of recently access configuration values mutable Mutex mLock; ///< control for multithreaded access to the cache + int mFacility; public: - ConfigurationTable(const char* filename = ":memory:"); + ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, int wFacility = LOG_USER); /** Return true if the key is used in the table. */ bool defines(const std::string& key); @@ -211,12 +216,34 @@ class ConfigurationTable { */ long getNum(const std::string& key); + /** + Get a boolean from the table. + Return false if NULL or 0, true otherwise. + */ + bool getBool(const std::string& key); + /** Get a numeric parameter from the table. Define the parameter to the default value if not found. */ long getNum(const std::string& key, long defaultValue); + /** + Get a vector of strings from the table. + */ + std::vector getVectorOfStrings(const std::string& key); + + /** + Get a vector of strings from the table, with a default value.. + */ + std::vector getVectorOfStrings(const std::string& key, const char* defaultValue); + + /** + Get a float from the table. + Throw ConfigurationTableKeyNotFound if not found. + */ + float getFloat(const std::string& key); + /** Get a numeric vector from the table. */ @@ -236,13 +263,21 @@ class ConfigurationTable { bool set(const std::string& key); /** - Remove a key from the table. - Will not remove static or required values. - @param key The key of the item to be deleted. - @return true if anything was actually removed. + Set a corresponding value to NULL. + Will not alter required values. + @param key The key of the item to be nulled-out. + @return true if anything was actually nulled-out. */ bool unset(const std::string& key); + /** + Remove an entry from the table. + Will not alter required values. + @param key The key of the item to be removed. + @return true if anything was actually removed. + */ + bool remove(const std::string& key); + /** Search the table, dumping to a stream. */ void find(const std::string& pattern, std::ostream&) const; @@ -268,6 +303,27 @@ class ConfigurationTable { }; +typedef std::map HashStringMap; + +class SimpleKeyValue { + + protected: + + HashStringMap mMap; + + public: + + /** Take a C string "A=B" and set map["A"]="B". */ + void addItem(const char*); + + /** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */ + void addItems(const char*s); + + /** Return a reference to the string at map["key"]. */ + const char* get(const char*) const; +}; + + #endif diff --git a/CommonLibs/ConfigurationTest.cpp b/CommonLibs/ConfigurationTest.cpp index ef82601e..5acb9d58 100644 --- a/CommonLibs/ConfigurationTest.cpp +++ b/CommonLibs/ConfigurationTest.cpp @@ -28,10 +28,11 @@ #include "Configuration.h" #include +#include using namespace std; -ConfigurationTable gConfig("exampleconfig.db"); +ConfigurationTable gConfig("exampleconfig.db","test",LOG_LOCAL7); void purgeConfig(void*,int,char const*, char const*, sqlite3_int64) { @@ -61,9 +62,44 @@ int main(int argc, char *argv[]) cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl; } - gConfig.set("key5","100 200 300 400"); + gConfig.set("key5","100 200 300 400 "); std::vector vect = gConfig.getVector("key5"); cout << "vect length " << vect.size() << ": "; for (unsigned i=0; i svect = gConfig.getVectorOfStrings("key5"); + cout << "vect length " << svect.size() << ": "; + for (unsigned i=0; i