mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-11-04 05:43:14 +00:00
syncing commonlibs with Many thanks to Michael Iedema for these patches, makes config a lot better.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@5655 19bc5d8c-e614-43d4-8b26-e1612bc8e597
This commit is contained in:
29
CLI/CLI.cpp
29
CLI/CLI.cpp
@@ -515,23 +515,30 @@ int config(int argc, char** argv, ostream& os)
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove a configiuration value. */
|
/** Disable a configuration key. */
|
||||||
int unconfig(int argc, char** argv, ostream& os)
|
int unconfig(int argc, char** argv, ostream& os)
|
||||||
{
|
{
|
||||||
if (argc!=2) return BAD_NUM_ARGS;
|
if (argc!=2) return BAD_NUM_ARGS;
|
||||||
|
|
||||||
if (gConfig.unset(argv[1])) {
|
if (!gConfig.defines(argv[1])) {
|
||||||
os << "\"" << argv[1] << "\" removed from the configuration table" << endl;
|
os << argv[1] << " is not in the table" << endl;
|
||||||
return SUCCESS;
|
return BAD_VALUE;
|
||||||
}
|
}
|
||||||
if (gConfig.defines(argv[1])) {
|
|
||||||
os << "\"" << argv[1] << "\" could not be removed" << endl;
|
|
||||||
} else {
|
|
||||||
os << "\"" << argv[1] << "\" was 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;
|
||||||
|
}
|
||||||
|
|
||||||
/** Dump current configuration to a file. */
|
/** Dump current configuration to a file. */
|
||||||
int configsave(int argc, char** argv, ostream& os)
|
int configsave(int argc, char** argv, ostream& os)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "BitVector.h"
|
#include "BitVector.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -274,9 +275,6 @@ void BitVector::unmap(const unsigned *map, size_t mapSize, BitVector& dest) cons
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ostream& operator<<(ostream& os, const BitVector& hv)
|
ostream& operator<<(ostream& os, const BitVector& hv)
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<hv.size(); i++) {
|
for (size_t i=0; i<hv.size(); i++) {
|
||||||
@@ -527,6 +525,22 @@ void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// (pat) Added 6-22-2012
|
||||||
|
float SoftVector::getEnergy(float *plow) const
|
||||||
|
{
|
||||||
|
const SoftVector &vec = *this;
|
||||||
|
int len = vec.size();
|
||||||
|
float avg = 0; float low = 1;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
float bit = vec[i];
|
||||||
|
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
|
||||||
|
if (energy < low) low = energy;
|
||||||
|
avg += energy/len;
|
||||||
|
}
|
||||||
|
if (plow) { *plow = low; }
|
||||||
|
return avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ostream& operator<<(ostream& os, const SoftVector& sv)
|
ostream& operator<<(ostream& os, const SoftVector& sv)
|
||||||
{
|
{
|
||||||
@@ -578,6 +592,14 @@ void BitVector::hex(ostream& os) const
|
|||||||
os << std::dec;
|
os << std::dec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BitVector::hexstr() const
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
hex(ss);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool BitVector::unhex(const char* src)
|
bool BitVector::unhex(const char* src)
|
||||||
{
|
{
|
||||||
// Assumes MSB-first packing.
|
// Assumes MSB-first packing.
|
||||||
|
|||||||
@@ -314,6 +314,9 @@ class BitVector : public Vector<char> {
|
|||||||
void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length);
|
void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length);
|
||||||
void writeField(size_t& writeIndex, uint64_t value, unsigned length);
|
void writeField(size_t& writeIndex, uint64_t value, unsigned length);
|
||||||
void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length);
|
void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length);
|
||||||
|
void write0(size_t& writeIndex) { writeField(writeIndex,0,1); }
|
||||||
|
void write1(size_t& writeIndex) { writeField(writeIndex,1,1); }
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/** Sum of bits. */
|
/** Sum of bits. */
|
||||||
@@ -333,11 +336,26 @@ class BitVector : public Vector<char> {
|
|||||||
|
|
||||||
/** Make a hexdump string. */
|
/** Make a hexdump string. */
|
||||||
void hex(std::ostream&) const;
|
void hex(std::ostream&) const;
|
||||||
|
std::string hexstr() const;
|
||||||
|
|
||||||
/** Unpack from a hexdump string.
|
/** Unpack from a hexdump string.
|
||||||
* @returns true on success, false on error. */
|
* @returns true on success, false on error. */
|
||||||
bool unhex(const char*);
|
bool unhex(const char*);
|
||||||
|
|
||||||
|
void set(BitVector other) // That's right. No ampersand.
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
mData=other.mData;
|
||||||
|
mStart=other.mStart;
|
||||||
|
mEnd=other.mEnd;
|
||||||
|
other.mData=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void settfb(int i, int j) const
|
||||||
|
{
|
||||||
|
mStart[i] = j;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -412,6 +430,11 @@ class SoftVector: public Vector<float> {
|
|||||||
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
||||||
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
|
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
|
||||||
|
|
||||||
|
// (pat) How good is the SoftVector in the sense of the bits being solid?
|
||||||
|
// Result of 1 is perfect and 0 means all the bits were 0.5
|
||||||
|
// If plow is non-NULL, also return the lowest energy bit.
|
||||||
|
float getEnergy(float *low=0) const;
|
||||||
|
|
||||||
/** Fill with "unknown" values. */
|
/** Fill with "unknown" values. */
|
||||||
void unknown() { fill(0.5F); }
|
void unknown() { fill(0.5F); }
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,10 +33,14 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <Threads.h>
|
#include <Threads.h>
|
||||||
@@ -165,8 +169,10 @@ class HashString : public std::string {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
|
||||||
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
|
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
|
||||||
|
class ConfigurationKey;
|
||||||
|
typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A class for maintaining a configuration key-value table,
|
A class for maintaining a configuration key-value table,
|
||||||
@@ -180,20 +186,37 @@ class ConfigurationTable {
|
|||||||
sqlite3* mDB; ///< database connection
|
sqlite3* mDB; ///< database connection
|
||||||
ConfigurationMap mCache; ///< cache of recently access configuration values
|
ConfigurationMap mCache; ///< cache of recently access configuration values
|
||||||
mutable Mutex mLock; ///< control for multithreaded access to the cache
|
mutable Mutex mLock; ///< control for multithreaded access to the cache
|
||||||
|
std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
|
||||||
|
|
||||||
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0);
|
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
|
||||||
|
|
||||||
|
/** Generate an up-to-date example sql file for new installs. */
|
||||||
|
std::string getDefaultSQL(const std::string& program, const std::string& version);
|
||||||
|
|
||||||
|
/** Generate an up-to-date TeX snippet. */
|
||||||
|
std::string getTeX(const std::string& program, const std::string& version);
|
||||||
|
|
||||||
/** Return true if the key is used in the table. */
|
/** Return true if the key is used in the table. */
|
||||||
bool defines(const std::string& key);
|
bool defines(const std::string& key);
|
||||||
|
|
||||||
/** Return true if this key is identified as static. */
|
/** Return true if the application's schema knows about this key. */
|
||||||
bool isStatic(const std::string& key) const;
|
bool keyDefinedInSchema(const std::string& name);
|
||||||
|
|
||||||
/** Return true if this key is identified as required (!optional). */
|
/** Return true if the provided value validates correctly against the defined schema. */
|
||||||
bool isRequired(const std::string& key) const;
|
bool isValidValue(const std::string& name, const std::string& val);
|
||||||
|
|
||||||
|
/** Return true if the provided value validates correctly against the defined schema. */
|
||||||
|
bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
|
||||||
|
|
||||||
|
/** Return a map of all similar keys in the defined schema. */
|
||||||
|
ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
|
||||||
|
|
||||||
|
/** Return true if this key is identified as static. */
|
||||||
|
bool isStatic(const std::string& key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a string parameter from the table.
|
Get a string parameter from the table.
|
||||||
@@ -203,11 +226,10 @@ class ConfigurationTable {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a string parameter from the table.
|
Get a boolean from the table.
|
||||||
Define the parameter to the default value if not found.
|
Return false if NULL or 0, true otherwise.
|
||||||
*/
|
*/
|
||||||
std::string getStr(const std::string& key, const char* defaultValue);
|
bool getBool(const std::string& key);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a numeric parameter from the table.
|
Get a numeric parameter from the table.
|
||||||
@@ -215,28 +237,11 @@ class ConfigurationTable {
|
|||||||
*/
|
*/
|
||||||
long getNum(const std::string& key);
|
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.
|
Get a vector of strings from the table.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getVectorOfStrings(const std::string& key);
|
std::vector<std::string> getVectorOfStrings(const std::string& key);
|
||||||
|
|
||||||
/**
|
|
||||||
Get a vector of strings from the table, with a default value..
|
|
||||||
*/
|
|
||||||
std::vector<std::string> getVectorOfStrings(const std::string& key, const char* defaultValue);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a float from the table.
|
Get a float from the table.
|
||||||
Throw ConfigurationTableKeyNotFound if not found.
|
Throw ConfigurationTableKeyNotFound if not found.
|
||||||
@@ -261,14 +266,6 @@ class ConfigurationTable {
|
|||||||
/** Create an entry in the table, no value though. */
|
/** Create an entry in the table, no value though. */
|
||||||
bool set(const std::string& key);
|
bool set(const std::string& key);
|
||||||
|
|
||||||
/**
|
|
||||||
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.
|
Remove an entry from the table.
|
||||||
Will not alter required values.
|
Will not alter required values.
|
||||||
@@ -280,9 +277,18 @@ class ConfigurationTable {
|
|||||||
/** Search the table, dumping to a stream. */
|
/** Search the table, dumping to a stream. */
|
||||||
void find(const std::string& pattern, std::ostream&) const;
|
void find(const std::string& pattern, std::ostream&) const;
|
||||||
|
|
||||||
|
/** Return all key/value pairs stored in the ConfigurationTable */
|
||||||
|
ConfigurationRecordMap getAllPairs() const;
|
||||||
|
|
||||||
/** Define the callback to purge the cache whenever the database changes. */
|
/** Define the callback to purge the cache whenever the database changes. */
|
||||||
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
|
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
|
||||||
|
|
||||||
|
/** Define the callback for cross checking. */
|
||||||
|
void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
|
||||||
|
|
||||||
|
/** Execute the application specific value cross checking logic. */
|
||||||
|
std::vector<std::string> crossCheck(const std::string& key);
|
||||||
|
|
||||||
/** purege cache if it exceeds a certain age */
|
/** purege cache if it exceeds a certain age */
|
||||||
void checkCacheAge();
|
void checkCacheAge();
|
||||||
|
|
||||||
@@ -323,6 +329,92 @@ class SimpleKeyValue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurationKey {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum VisibilityLevel
|
||||||
|
{
|
||||||
|
CUSTOMER,
|
||||||
|
CUSTOMERSITE,
|
||||||
|
CUSTOMERTUNE,
|
||||||
|
CUSTOMERWARN,
|
||||||
|
DEVELOPER,
|
||||||
|
FACTORY
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
BOOLEAN,
|
||||||
|
CHOICE_OPT,
|
||||||
|
CHOICE,
|
||||||
|
CIDR_OPT,
|
||||||
|
CIDR,
|
||||||
|
FILEPATH_OPT,
|
||||||
|
FILEPATH,
|
||||||
|
IPADDRESS_OPT,
|
||||||
|
IPADDRESS,
|
||||||
|
IPANDPORT,
|
||||||
|
MIPADDRESS_OPT,
|
||||||
|
MIPADDRESS,
|
||||||
|
PORT_OPT,
|
||||||
|
PORT,
|
||||||
|
REGEX_OPT,
|
||||||
|
REGEX,
|
||||||
|
STRING_OPT,
|
||||||
|
STRING,
|
||||||
|
VALRANGE
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string mName;
|
||||||
|
std::string mDefaultValue;
|
||||||
|
std::string mUnits;
|
||||||
|
VisibilityLevel mVisibility;
|
||||||
|
Type mType;
|
||||||
|
std::string mValidValues;
|
||||||
|
bool mIsStatic;
|
||||||
|
std::string mDescription;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
|
||||||
|
mName(wName),
|
||||||
|
mDefaultValue(wDefaultValue),
|
||||||
|
mUnits(wUnits),
|
||||||
|
mVisibility(wVisibility),
|
||||||
|
mType(wType),
|
||||||
|
mValidValues(wValidValues),
|
||||||
|
mIsStatic(wIsStatic),
|
||||||
|
mDescription(wDescription)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ConfigurationKey()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
const std::string& getName() const { return mName; }
|
||||||
|
const std::string& getDefaultValue() const { return mDefaultValue; }
|
||||||
|
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
|
||||||
|
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
|
||||||
|
const std::string& getUnits() const { return mUnits; }
|
||||||
|
const VisibilityLevel& getVisibility() const { return mVisibility; }
|
||||||
|
const Type& getType() const { return mType; }
|
||||||
|
const std::string& getValidValues() const { return mValidValues; }
|
||||||
|
bool isStatic() const { return mIsStatic; }
|
||||||
|
const std::string& getDescription() const { return mDescription; }
|
||||||
|
|
||||||
|
static bool isValidIP(const std::string& ip);
|
||||||
|
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
|
||||||
|
template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
|
||||||
|
static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
|
||||||
|
static const std::string typeToString(const ConfigurationKey::Type& type);
|
||||||
|
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
|
||||||
|
static void printDescription(const ConfigurationKey &key, std::ostream& os);
|
||||||
|
static const std::string getARFCNsString();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ConfigurationTable gConfig("exampleconfig.db","test");
|
ConfigurationKeyMap getConfigurationKeys();
|
||||||
|
ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
|
||||||
|
|
||||||
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
|
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
|
||||||
{
|
{
|
||||||
@@ -46,7 +47,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
gConfig.setUpdateHook(purgeConfig);
|
gConfig.setUpdateHook(purgeConfig);
|
||||||
|
|
||||||
const char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
||||||
|
|
||||||
for (int i=0; i<5; i++) {
|
for (int i=0; i<5; i++) {
|
||||||
gConfig.set(keys[i],i);
|
gConfig.set(keys[i],i);
|
||||||
@@ -57,7 +58,6 @@ int main(int argc, char *argv[])
|
|||||||
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
|
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
gConfig.unset("key1");
|
|
||||||
for (int i=0; i<5; i++) {
|
for (int i=0; i<5; i++) {
|
||||||
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
|
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ int main(int argc, char *argv[])
|
|||||||
gConfig.set("booltest",0);
|
gConfig.set("booltest",0);
|
||||||
cout << "bool " << gConfig.getBool("booltest") << endl;
|
cout << "bool " << gConfig.getBool("booltest") << endl;
|
||||||
|
|
||||||
gConfig.getStr("newstring","new string value");
|
gConfig.getStr("newstring");
|
||||||
gConfig.getNum("numnumber",42);
|
gConfig.getNum("numnumber");
|
||||||
|
|
||||||
|
|
||||||
SimpleKeyValue pairs;
|
SimpleKeyValue pairs;
|
||||||
@@ -94,7 +94,6 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
cout << "search fkey:" << endl;
|
cout << "search fkey:" << endl;
|
||||||
gConfig.find("fkey",cout);
|
gConfig.find("fkey",cout);
|
||||||
gConfig.unset("fkey");
|
|
||||||
cout << "search fkey:" << endl;
|
cout << "search fkey:" << endl;
|
||||||
gConfig.find("fkey",cout);
|
gConfig.find("fkey",cout);
|
||||||
gConfig.remove("fkey");
|
gConfig.remove("fkey");
|
||||||
@@ -107,3 +106,44 @@ int main(int argc, char *argv[])
|
|||||||
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
|
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigurationKeyMap getConfigurationKeys()
|
||||||
|
{
|
||||||
|
ConfigurationKeyMap map;
|
||||||
|
ConfigurationKey *tmp;
|
||||||
|
|
||||||
|
tmp = new ConfigurationKey("booltest","0",
|
||||||
|
"",
|
||||||
|
ConfigurationKey::DEVELOPER,
|
||||||
|
ConfigurationKey::BOOLEAN,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
map[tmp->getName()] = *tmp;
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
|
tmp = new ConfigurationKey("numnumber","42",
|
||||||
|
"",
|
||||||
|
ConfigurationKey::DEVELOPER,
|
||||||
|
ConfigurationKey::VALRANGE,
|
||||||
|
"0-100",
|
||||||
|
false,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
map[tmp->getName()] = *tmp;
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
|
tmp = new ConfigurationKey("newstring","new string value",
|
||||||
|
"",
|
||||||
|
ConfigurationKey::DEVELOPER,
|
||||||
|
ConfigurationKey::STRING,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
map[tmp->getName()] = *tmp;
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,15 +42,21 @@
|
|||||||
|
|
||||||
|
|
||||||
/** Pointer FIFO for interthread operations. */
|
/** Pointer FIFO for interthread operations. */
|
||||||
template <class T> class InterthreadQueue {
|
// (pat) The elements in the queue are type T*, and
|
||||||
|
// the Fifo class implements the underlying queue.
|
||||||
|
// The default is class PointerFIFO, which does not place any restrictions on the type of T,
|
||||||
|
// and is implemented by allocating auxilliary structures for the queue,
|
||||||
|
// or SingleLinkedList, which implements the queue using an internal pointer in type T,
|
||||||
|
// which must implement the functional interface of class SingleLinkListNode,
|
||||||
|
// namely: functions T*next() and void setNext(T*).
|
||||||
|
template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
PointerFIFO mQ;
|
Fifo mQ;
|
||||||
mutable Mutex mLock;
|
mutable Mutex mLock;
|
||||||
mutable Signal mWriteSignal;
|
mutable Signal mWriteSignal;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Delete contents. */
|
/** Delete contents. */
|
||||||
@@ -78,6 +84,12 @@ template <class T> class InterthreadQueue {
|
|||||||
return mQ.size();
|
return mQ.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t totalSize() const // pat added
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
return mQ.totalSize();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Blocking read.
|
Blocking read.
|
||||||
@return Pointer to object (will not be NULL).
|
@return Pointer to object (will not be NULL).
|
||||||
@@ -93,6 +105,13 @@ template <class T> class InterthreadQueue {
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Non-blocking peek at the first element; returns NULL if empty. */
|
||||||
|
T* front()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
return (T*) mQ.front();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Blocking read with a timeout.
|
Blocking read with a timeout.
|
||||||
@param timeout The read timeout in ms.
|
@param timeout The read timeout in ms.
|
||||||
@@ -127,7 +146,132 @@ template <class T> class InterthreadQueue {
|
|||||||
mWriteSignal.signal();
|
mWriteSignal.signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Non-block write to the front of the queue. */
|
||||||
|
void write_front(T* val) // pat added
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
mQ.push_front(val);
|
||||||
|
mWriteSignal.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// (pat) Identical to above but with the threading problem fixed.
|
||||||
|
template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Fifo mQ;
|
||||||
|
mutable Mutex mLock;
|
||||||
|
mutable Signal mWriteSignal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** Delete contents. */
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
while (mQ.size()>0) delete (T*)mQ.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Empty the queue, but don't delete. */
|
||||||
|
void flushNoDelete()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
while (mQ.size()>0) mQ.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
~InterthreadQueue2()
|
||||||
|
{ clear(); }
|
||||||
|
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
return mQ.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t totalSize() const // pat added
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
return mQ.totalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Blocking read.
|
||||||
|
@return Pointer to object (will not be NULL).
|
||||||
|
*/
|
||||||
|
T* read()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
T* retVal = (T*)mQ.get();
|
||||||
|
while (retVal==NULL) {
|
||||||
|
mWriteSignal.wait(mLock);
|
||||||
|
retVal = (T*)mQ.get();
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Non-blocking peek at the first element; returns NULL if empty. */
|
||||||
|
T* front()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
return (T*) mQ.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Blocking read with a timeout.
|
||||||
|
@param timeout The read timeout in ms.
|
||||||
|
@return Pointer to object or NULL on timeout.
|
||||||
|
*/
|
||||||
|
T* read(unsigned timeout)
|
||||||
|
{
|
||||||
|
if (timeout==0) return readNoBlock();
|
||||||
|
Timeval waitTime(timeout);
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
while ((mQ.size()==0) && (!waitTime.passed()))
|
||||||
|
mWriteSignal.wait(mLock,waitTime.remaining());
|
||||||
|
T* retVal = (T*)mQ.get();
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Non-blocking read.
|
||||||
|
@return Pointer to object or NULL if FIFO is empty.
|
||||||
|
*/
|
||||||
|
T* readNoBlock()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
return (T*)mQ.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Non-blocking write. */
|
||||||
|
void write(T* val)
|
||||||
|
{
|
||||||
|
// (pat) The Mutex mLock must be released before signaling the mWriteSignal condition.
|
||||||
|
// This is an implicit requirement of pthread_cond_wait() called from signal().
|
||||||
|
// If you do not do that, the InterthreadQueue read() function cannot start
|
||||||
|
// because the mutex is still locked by the thread calling the write(),
|
||||||
|
// so the read() thread yields its immediate execution opportunity.
|
||||||
|
// This recurs (and the InterthreadQueue fills up with data)
|
||||||
|
// until the read thread's accumulated temporary priority causes it to
|
||||||
|
// get a second pre-emptive activation over the writing thread,
|
||||||
|
// resulting in bursts of activity by the read thread.
|
||||||
|
{ ScopedLock lock(mLock);
|
||||||
|
mQ.put(val);
|
||||||
|
}
|
||||||
|
mWriteSignal.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Non-block write to the front of the queue. */
|
||||||
|
void write_front(T* val) // pat added
|
||||||
|
{
|
||||||
|
// (pat) See comments above.
|
||||||
|
{ ScopedLock lock(mLock);
|
||||||
|
mQ.push_front(val);
|
||||||
|
}
|
||||||
|
mWriteSignal.signal();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -214,12 +358,17 @@ template <class T> class InterthreadQueueWithWait {
|
|||||||
/** Non-blocking write. */
|
/** Non-blocking write. */
|
||||||
void write(T* val)
|
void write(T* val)
|
||||||
{
|
{
|
||||||
|
// (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field.
|
||||||
ScopedLock lock(mLock);
|
ScopedLock lock(mLock);
|
||||||
mQ.put(val);
|
mQ.put(val);
|
||||||
mWriteSignal.signal();
|
mWriteSignal.signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wait until the queue falls below a low water mark. */
|
/** Wait until the queue falls below a low water mark. */
|
||||||
|
// (pat) This function suffers from the same problem as documented
|
||||||
|
// at InterthreadQueue.write(), but I am not fixing it because I cannot test it.
|
||||||
|
// The caller of this function will eventually get to run, just not immediately
|
||||||
|
// after the mReadSignal condition is fulfilled.
|
||||||
void wait(size_t sz=0)
|
void wait(size_t sz=0)
|
||||||
{
|
{
|
||||||
ScopedLock lock(mLock);
|
ScopedLock lock(mLock);
|
||||||
@@ -484,6 +633,7 @@ template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > cl
|
|||||||
/** Non-blocking write. */
|
/** Non-blocking write. */
|
||||||
void write(T* val)
|
void write(T* val)
|
||||||
{
|
{
|
||||||
|
// (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field.
|
||||||
ScopedLock lock(mLock);
|
ScopedLock lock(mLock);
|
||||||
mQ.push(val);
|
mQ.push(val);
|
||||||
mWriteSignal.signal();
|
mWriteSignal.signal();
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ void* mapReader(void*)
|
|||||||
for (int i=0; i<20; i++) {
|
for (int i=0; i<20; i++) {
|
||||||
int *p = gMap.read(i);
|
int *p = gMap.read(i);
|
||||||
COUT("map read " << *p);
|
COUT("map read " << *p);
|
||||||
delete p;
|
// InterthreadMap will delete the pointers
|
||||||
|
// delete p;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,17 @@
|
|||||||
#include "LinkedLists.h"
|
#include "LinkedLists.h"
|
||||||
|
|
||||||
|
|
||||||
|
void PointerFIFO::push_front(void* val) // by pat
|
||||||
|
{
|
||||||
|
// Pat added this routine for completeness, but never used or tested.
|
||||||
|
// The first person to use this routine should remove this assert.
|
||||||
|
ListNode *node = allocate();
|
||||||
|
node->data(val);
|
||||||
|
node->next(mHead);
|
||||||
|
mHead = node;
|
||||||
|
if (!mTail) mTail=node;
|
||||||
|
mSize++;
|
||||||
|
}
|
||||||
|
|
||||||
void PointerFIFO::put(void* val)
|
void PointerFIFO::put(void* val)
|
||||||
{
|
{
|
||||||
@@ -58,7 +68,6 @@ void* PointerFIFO::get()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ListNode *PointerFIFO::allocate()
|
ListNode *PointerFIFO::allocate()
|
||||||
{
|
{
|
||||||
if (mFreeList==NULL) return new ListNode;
|
if (mFreeList==NULL) return new ListNode;
|
||||||
@@ -72,6 +81,3 @@ void PointerFIFO::release(ListNode* wNode)
|
|||||||
wNode->next(mFreeList);
|
wNode->next(mFreeList);
|
||||||
mFreeList = wNode;
|
mFreeList = wNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2008 Free Software Foundation, Inc.
|
* Copyright 2008 Free Software Foundation, Inc.
|
||||||
*
|
*
|
||||||
|
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||||
|
*
|
||||||
* This software is distributed under the terms of the GNU Affero Public License.
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
* See the COPYING file in the main directory for details.
|
* See the COPYING file in the main directory for details.
|
||||||
*
|
*
|
||||||
@@ -28,6 +30,7 @@
|
|||||||
#define LINKEDLISTS_H
|
#define LINKEDLISTS_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -54,7 +57,7 @@ class ListNode {
|
|||||||
/** A fast FIFO for pointer-based storage. */
|
/** A fast FIFO for pointer-based storage. */
|
||||||
class PointerFIFO {
|
class PointerFIFO {
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
ListNode* mHead; ///< points to next item out
|
ListNode* mHead; ///< points to next item out
|
||||||
ListNode* mTail; ///< points to last item in
|
ListNode* mTail; ///< points to last item in
|
||||||
@@ -69,9 +72,12 @@ class PointerFIFO {
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
unsigned size() const { return mSize; }
|
unsigned size() const { return mSize; }
|
||||||
|
unsigned totalSize() const { return 0; } // Not used in this version.
|
||||||
|
|
||||||
/** Put an item into the FIFO. */
|
/** Put an item into the FIFO at the back of the queue. */
|
||||||
void put(void* val);
|
void put(void* val);
|
||||||
|
/** Push an item on the front of the FIFO. */
|
||||||
|
void push_front(void*val); // pat added.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Take an item from the FIFO.
|
Take an item from the FIFO.
|
||||||
@@ -79,6 +85,9 @@ class PointerFIFO {
|
|||||||
*/
|
*/
|
||||||
void* get();
|
void* get();
|
||||||
|
|
||||||
|
/** Peek at front item without removal. */
|
||||||
|
void *front() { return mHead ? mHead->data() : 0; } // pat added
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -90,6 +99,74 @@ class PointerFIFO {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is the default type for SingleLinkList Node element;
|
||||||
|
// You can derive your class directly from this, but then you must add type casts
|
||||||
|
// all over the place.
|
||||||
|
class SingleLinkListNode
|
||||||
|
{ public:
|
||||||
|
SingleLinkListNode *mNext;
|
||||||
|
SingleLinkListNode *next() {return mNext;}
|
||||||
|
void setNext(SingleLinkListNode *item) {mNext=item;}
|
||||||
|
SingleLinkListNode() : mNext(0) {}
|
||||||
|
virtual unsigned size() { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A single-linked lists of elements with internal pointers.
|
||||||
|
// The methods must match those from SingleLinkListNode.
|
||||||
|
// This class also assumes the Node has a size() method, and accumulates
|
||||||
|
// the total size of elements in the list in totalSize().
|
||||||
|
template<class Node=SingleLinkListNode>
|
||||||
|
class SingleLinkList
|
||||||
|
{
|
||||||
|
Node *mHead, *mTail;
|
||||||
|
unsigned mSize; // Number of elements in list.
|
||||||
|
unsigned mTotalSize; // Total of size() method of elements in list.
|
||||||
|
|
||||||
|
public:
|
||||||
|
SingleLinkList() : mHead(0), mTail(0), mSize(0), mTotalSize(0) {}
|
||||||
|
unsigned size() const { return mSize; }
|
||||||
|
unsigned totalSize() const { return mTotalSize; }
|
||||||
|
|
||||||
|
Node *pop_back() { assert(0); } // Not efficient with this type of list.
|
||||||
|
|
||||||
|
Node *pop_front()
|
||||||
|
{
|
||||||
|
if (!mHead) return NULL;
|
||||||
|
Node *result = mHead;
|
||||||
|
mHead = mHead->next();
|
||||||
|
if (mTail == result) { mTail = NULL; assert(mHead == NULL); }
|
||||||
|
result->setNext(NULL); // be neat
|
||||||
|
mSize--;
|
||||||
|
mTotalSize -= result->size();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_front(Node *item)
|
||||||
|
{
|
||||||
|
item->setNext(mHead);
|
||||||
|
mHead = item;
|
||||||
|
if (!mTail) { mTail = item; }
|
||||||
|
mSize++;
|
||||||
|
mTotalSize += item->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(Node *item)
|
||||||
|
{
|
||||||
|
item->setNext(NULL);
|
||||||
|
if (mTail) { mTail->setNext(item); }
|
||||||
|
mTail = item;
|
||||||
|
if (!mHead) mHead = item;
|
||||||
|
mSize++;
|
||||||
|
mTotalSize += item->size();
|
||||||
|
}
|
||||||
|
Node *front() { return mHead; }
|
||||||
|
Node *back() { return mTail; }
|
||||||
|
|
||||||
|
// Interface to InterthreadQueue so it can used SingleLinkList as the Fifo.
|
||||||
|
void put(void *val) { push_back((Node*)val); }
|
||||||
|
void *get() { return pop_front(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ void printAlarms()
|
|||||||
{
|
{
|
||||||
std::ostream_iterator<std::string> output( std::cout, "\n" );
|
std::ostream_iterator<std::string> output( std::cout, "\n" );
|
||||||
std::list<std::string> alarms = gGetLoggerAlarms();
|
std::list<std::string> alarms = gGetLoggerAlarms();
|
||||||
std::cout << "#alarms = " << alarms.size() << std::endl;
|
std::cout << "# alarms = " << alarms.size() << std::endl;
|
||||||
std::copy( alarms.begin(), alarms.end(), output );
|
std::copy( alarms.begin(), alarms.end(), output );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ int main(int argc, char *argv[])
|
|||||||
LOG(DEBUG) << " testing the logger.";
|
LOG(DEBUG) << " testing the logger.";
|
||||||
std::cout << "\n\n\n";
|
std::cout << "\n\n\n";
|
||||||
std::cout << "testing Alarms\n";
|
std::cout << "testing Alarms\n";
|
||||||
LOG(ALERT) << " testing the logger alarm.";
|
|
||||||
std::cout << "you should see three lines:" << std::endl;
|
std::cout << "you should see three lines:" << std::endl;
|
||||||
printAlarms();
|
printAlarms();
|
||||||
std::cout << "----------- generating 20 alarms ----------" << std::endl;
|
std::cout << "----------- generating 20 alarms ----------" << std::endl;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||||
* Copyright 2010 Kestrel Signal Processing, 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.
|
* This software is distributed under the terms of the GNU Affero Public License.
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
|
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Threads.h" // pat added
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -49,36 +51,71 @@ void addAlarm(const string&);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// (pat) If Log messages are printed before the classes in this module are inited
|
||||||
|
// (which happens when static classes have constructors that do work)
|
||||||
|
// the OpenBTS just crashes.
|
||||||
|
// Prevent that by setting sLoggerInited to true when this module is inited.
|
||||||
|
static bool sLoggerInited = 0;
|
||||||
|
static struct CheckLoggerInitStatus {
|
||||||
|
CheckLoggerInitStatus() { sLoggerInited = 1; }
|
||||||
|
} sCheckloggerInitStatus;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Names of the logging levels. */
|
/** Names of the logging levels. */
|
||||||
const char *levelNames[] = {
|
const char *levelNames[] = {
|
||||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||||
};
|
};
|
||||||
int numLevels = 8;
|
int numLevels = 8;
|
||||||
|
bool gLogToConsole = 0;
|
||||||
|
FILE *gLogToFile = NULL;
|
||||||
|
Mutex gLogToLock;
|
||||||
|
|
||||||
|
|
||||||
/** Given a string, return the corresponding level name. */
|
int levelStringToInt(const string& name)
|
||||||
int lookupLevel(const string& name)
|
|
||||||
{
|
{
|
||||||
// Reverse search, since the numerically larger levels are more common.
|
// Reverse search, since the numerically larger levels are more common.
|
||||||
for (int i=numLevels-1; i>=0; i--) {
|
for (int i=numLevels-1; i>=0; i--) {
|
||||||
if (name == levelNames[i]) return i;
|
if (name == levelNames[i]) return i;
|
||||||
}
|
}
|
||||||
// This should never be called with a bogus name.
|
|
||||||
LOG(ERR) << "undefined logging level " << name << "defaulting to ERR";
|
// Common substitutions.
|
||||||
return LOG_ERR;
|
if (name=="INFORMATION") return 6;
|
||||||
|
if (name=="WARN") return 4;
|
||||||
|
if (name=="ERROR") return 3;
|
||||||
|
if (name=="CRITICAL") return 2;
|
||||||
|
if (name=="EMERGENCY") return 0;
|
||||||
|
|
||||||
|
// Unknown level.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given a string, return the corresponding level name. */
|
||||||
|
int lookupLevel(const string& key)
|
||||||
|
{
|
||||||
|
string val = gConfig.getStr(key);
|
||||||
|
int level = levelStringToInt(val);
|
||||||
|
|
||||||
|
if (level == -1) {
|
||||||
|
string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
|
||||||
|
level = levelStringToInt(defaultLevel);
|
||||||
|
_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
|
||||||
|
gConfig.set(key, defaultLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int getLoggingLevel(const char* filename)
|
int getLoggingLevel(const char* filename)
|
||||||
{
|
{
|
||||||
// Default level?
|
// Default level?
|
||||||
if (!filename) return lookupLevel(gConfig.getStr("Log.Level"));
|
if (!filename) return lookupLevel("Log.Level");
|
||||||
|
|
||||||
// This can afford to be inefficient since it is not called that often.
|
// This can afford to be inefficient since it is not called that often.
|
||||||
const string keyName = string("Log.Level.") + string(filename);
|
const string keyName = string("Log.Level.") + string(filename);
|
||||||
if (gConfig.defines(keyName)) return lookupLevel(gConfig.getStr(keyName));
|
if (gConfig.defines(keyName)) return lookupLevel(keyName);
|
||||||
return lookupLevel(gConfig.getStr("Log.Level"));
|
return lookupLevel("Log.Level");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -113,6 +150,7 @@ int gGetLoggingLevel(const char* filename)
|
|||||||
}
|
}
|
||||||
// Look it up in the config table and cache it.
|
// Look it up in the config table and cache it.
|
||||||
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
|
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
|
||||||
|
// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
|
||||||
sLogCacheLock.unlock();
|
sLogCacheLock.unlock();
|
||||||
int level = getLoggingLevel(filename);
|
int level = getLoggingLevel(filename);
|
||||||
sLogCacheLock.lock();
|
sLogCacheLock.lock();
|
||||||
@@ -155,12 +193,30 @@ Log::~Log()
|
|||||||
// Anything at or above LOG_CRIT is an "alarm".
|
// Anything at or above LOG_CRIT is an "alarm".
|
||||||
// Save alarms in the local list and echo them to stderr.
|
// Save alarms in the local list and echo them to stderr.
|
||||||
if (mPriority <= LOG_CRIT) {
|
if (mPriority <= LOG_CRIT) {
|
||||||
addAlarm(mStream.str().c_str());
|
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||||
cerr << mStream.str() << endl;
|
cerr << mStream.str() << endl;
|
||||||
}
|
}
|
||||||
// Current logging level was already checked by the macro.
|
// Current logging level was already checked by the macro.
|
||||||
// So just log.
|
// So just log.
|
||||||
syslog(mPriority, "%s", mStream.str().c_str());
|
syslog(mPriority, "%s", mStream.str().c_str());
|
||||||
|
// pat added for easy debugging.
|
||||||
|
if (gLogToConsole||gLogToFile) {
|
||||||
|
int mlen = mStream.str().size();
|
||||||
|
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||||
|
gLogToLock.lock();
|
||||||
|
if (gLogToConsole) {
|
||||||
|
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||||
|
// so just use std::cout.
|
||||||
|
std::cout << mStream.str();
|
||||||
|
if (neednl) std::cout<<"\n";
|
||||||
|
}
|
||||||
|
if (gLogToFile) {
|
||||||
|
fputs(mStream.str().c_str(),gLogToFile);
|
||||||
|
if (neednl) {fputc('\n',gLogToFile);}
|
||||||
|
fflush(gLogToFile);
|
||||||
|
}
|
||||||
|
gLogToLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -182,18 +238,26 @@ ostringstream& Log::get()
|
|||||||
|
|
||||||
void gLogInit(const char* name, const char* level, int facility)
|
void gLogInit(const char* name, const char* level, int facility)
|
||||||
{
|
{
|
||||||
// Set the level.
|
// Set the level if one has been specified.
|
||||||
if (level) {
|
if (level) {
|
||||||
gConfig.set("Log.Level",level);
|
gConfig.set("Log.Level",level);
|
||||||
} else {
|
|
||||||
if (!gConfig.defines("Log.Level")) {
|
|
||||||
gConfig.set("Log.Level","WARNING");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define other logging parameters in the global config.
|
// Pat added, tired of the syslog facility.
|
||||||
if (!gConfig.defines("Log.Alarms.Max")) {
|
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
||||||
gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
|
string str = gConfig.getStr("Log.File");
|
||||||
|
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||||
|
const char *fn = str.c_str();
|
||||||
|
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
||||||
|
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
||||||
|
if (gLogToFile) {
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
fprintf(gLogToFile,"Starting at %s",ctime(&now));
|
||||||
|
fflush(gLogToFile);
|
||||||
|
std::cout << "Logging to file: " << fn << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the log connection.
|
// Open the log connection.
|
||||||
|
|||||||
@@ -23,6 +23,11 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h.
|
||||||
|
// This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file.
|
||||||
|
#ifdef WARNING
|
||||||
|
#undef WARNING
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef LOGGER_H
|
#ifndef LOGGER_H
|
||||||
#define LOGGER_H
|
#define LOGGER_H
|
||||||
@@ -34,21 +39,42 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "Threads.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define _LOG(level) \
|
#define _LOG(level) \
|
||||||
Log(LOG_##level).get() << pthread_self() \
|
Log(LOG_##level).get() << pthread_self() \
|
||||||
<< " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
||||||
|
|
||||||
|
#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define LOG(wLevel) \
|
#define LOG(wLevel) \
|
||||||
if (LOG_##wLevel!=LOG_DEBUG && gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel)
|
if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
||||||
#else
|
#else
|
||||||
#define LOG(wLevel) \
|
#define LOG(wLevel) \
|
||||||
if (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel)
|
if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// pat: And for your edification here are the 'levels' as defined in syslog.h:
|
||||||
|
// LOG_EMERG 0 system is unusable
|
||||||
|
// LOG_ALERT 1 action must be taken immediately
|
||||||
|
// LOG_CRIT 2 critical conditions
|
||||||
|
// LOG_ERR 3 error conditions
|
||||||
|
// LOG_WARNING 4 warning conditions
|
||||||
|
// LOG_NOTICE 5 normal, but significant, condition
|
||||||
|
// LOG_INFO 6 informational message
|
||||||
|
// LOG_DEBUG 7 debug-level message
|
||||||
|
|
||||||
|
// (pat) added - print out a var and its name.
|
||||||
|
// Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name);
|
||||||
|
#define LOGVAR2(name,val) " " << name << "=" << (val)
|
||||||
|
#define LOGVAR(var) (" " #var "=") << var
|
||||||
|
#define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec
|
||||||
|
#define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec
|
||||||
|
// These are kind of cheesy, but you can use for bitvector
|
||||||
|
#define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")"
|
||||||
|
#define LOGBV(bv) LOGBV2(#bv,bv)
|
||||||
|
#define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")"
|
||||||
|
|
||||||
|
|
||||||
#define OBJLOG(wLevel) \
|
#define OBJLOG(wLevel) \
|
||||||
LOG(wLevel) << "obj: " << this << ' '
|
LOG(wLevel) << "obj: " << this << ' '
|
||||||
@@ -56,8 +82,8 @@
|
|||||||
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
|
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
|
||||||
|
|
||||||
|
|
||||||
#define DEFAULT_MAX_ALARMS 10
|
#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A C++ stream-based thread-safe logger.
|
A C++ stream-based thread-safe logger.
|
||||||
@@ -90,6 +116,7 @@ class Log {
|
|||||||
|
|
||||||
std::ostringstream& get();
|
std::ostringstream& get();
|
||||||
};
|
};
|
||||||
|
extern bool gLogToConsole; // Pat added for easy debugging.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,11 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||||
AM_CXXFLAGS = -Wall -ldl -O3 -g -lpthread
|
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
example.config \
|
||||||
|
README.common
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libcommon.la
|
noinst_LTLIBRARIES = libcommon.la
|
||||||
|
|
||||||
@@ -32,11 +36,12 @@ libcommon_la_SOURCES = \
|
|||||||
Sockets.cpp \
|
Sockets.cpp \
|
||||||
Threads.cpp \
|
Threads.cpp \
|
||||||
Timeval.cpp \
|
Timeval.cpp \
|
||||||
|
Reporting.cpp \
|
||||||
|
Logger.cpp \
|
||||||
Configuration.cpp \
|
Configuration.cpp \
|
||||||
sqlite3util.cpp \
|
sqlite3util.cpp \
|
||||||
Logger.cpp \
|
|
||||||
URLEncode.cpp \
|
URLEncode.cpp \
|
||||||
Reporting.cpp
|
Utils.cpp
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
BitVectorTest \
|
BitVectorTest \
|
||||||
@@ -47,9 +52,10 @@ noinst_PROGRAMS = \
|
|||||||
VectorTest \
|
VectorTest \
|
||||||
ConfigurationTest \
|
ConfigurationTest \
|
||||||
LogTest \
|
LogTest \
|
||||||
|
URLEncodeTest \
|
||||||
F16Test
|
F16Test
|
||||||
|
|
||||||
# ReportingTest
|
# ReportingTest
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
BitVector.h \
|
BitVector.h \
|
||||||
@@ -60,15 +66,19 @@ noinst_HEADERS = \
|
|||||||
Timeval.h \
|
Timeval.h \
|
||||||
Regexp.h \
|
Regexp.h \
|
||||||
Vector.h \
|
Vector.h \
|
||||||
URLEncode.h \
|
|
||||||
Configuration.h \
|
Configuration.h \
|
||||||
Reporting.h \
|
Reporting.h \
|
||||||
F16.h \
|
F16.h \
|
||||||
|
URLEncode.h \
|
||||||
|
Utils.h \
|
||||||
Logger.h \
|
Logger.h \
|
||||||
sqlite3util.h
|
sqlite3util.h
|
||||||
|
|
||||||
|
URLEncodeTest_SOURCES = URLEncodeTest.cpp
|
||||||
|
URLEncodeTest_LDADD = libcommon.la
|
||||||
|
|
||||||
BitVectorTest_SOURCES = BitVectorTest.cpp
|
BitVectorTest_SOURCES = BitVectorTest.cpp
|
||||||
BitVectorTest_LDADD = libcommon.la
|
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||||
|
|
||||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||||
InterthreadTest_LDADD = libcommon.la
|
InterthreadTest_LDADD = libcommon.la
|
||||||
@@ -82,7 +92,7 @@ TimevalTest_SOURCES = TimevalTest.cpp
|
|||||||
TimevalTest_LDADD = libcommon.la
|
TimevalTest_LDADD = libcommon.la
|
||||||
|
|
||||||
VectorTest_SOURCES = VectorTest.cpp
|
VectorTest_SOURCES = VectorTest.cpp
|
||||||
VectorTest_LDADD = libcommon.la
|
VectorTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||||
|
|
||||||
RegexpTest_SOURCES = RegexpTest.cpp
|
RegexpTest_SOURCES = RegexpTest.cpp
|
||||||
RegexpTest_LDADD = libcommon.la
|
RegexpTest_LDADD = libcommon.la
|
||||||
@@ -90,8 +100,8 @@ RegexpTest_LDADD = libcommon.la
|
|||||||
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
||||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||||
|
|
||||||
#ReportingTest_SOURCES = ReportingTest.cpp
|
# ReportingTest_SOURCES = ReportingTest.cpp
|
||||||
#ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
|
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||||
|
|
||||||
LogTest_SOURCES = LogTest.cpp
|
LogTest_SOURCES = LogTest.cpp
|
||||||
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||||
|
|||||||
111
CommonLibs/MemoryLeak.h
Normal file
111
CommonLibs/MemoryLeak.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Range Networks, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This software is distributed under multiple licenses;
|
||||||
|
* see the COPYING file in the main directory for licensing
|
||||||
|
* information for this specific distribuion.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
#ifndef _MEMORYLEAK_
|
||||||
|
#define _MEMORYLEAK_ 1
|
||||||
|
#include <map>
|
||||||
|
#include "ScalarTypes.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
struct MemStats {
|
||||||
|
// Enumerates the classes that are checked.
|
||||||
|
// Redundancies are ok, for example, we check BitVector and also
|
||||||
|
// several descendants of BitVector.
|
||||||
|
enum MemoryNames {
|
||||||
|
mZeroIsUnused,
|
||||||
|
mVector,
|
||||||
|
mVectorData,
|
||||||
|
mBitVector,
|
||||||
|
mByteVector,
|
||||||
|
mByteVectorData,
|
||||||
|
mRLCRawBlock,
|
||||||
|
mRLCUplinkDataBlock,
|
||||||
|
mRLCMessage,
|
||||||
|
mRLCMsgPacketDownlinkDummyControlBlock, // Redundant with RLCMessage
|
||||||
|
mTBF,
|
||||||
|
mLlcEngine,
|
||||||
|
mSgsnDownlinkMsg,
|
||||||
|
mRachInfo,
|
||||||
|
mPdpPdu,
|
||||||
|
mFECDispatchInfo,
|
||||||
|
mL3Frame,
|
||||||
|
msignalVector,
|
||||||
|
mSoftVector,
|
||||||
|
mScramblingCode,
|
||||||
|
mURlcDownSdu,
|
||||||
|
mURlcPdu,
|
||||||
|
// Must be last:
|
||||||
|
mMax,
|
||||||
|
};
|
||||||
|
int mMemTotal[mMax]; // In elements, not bytes.
|
||||||
|
int mMemNow[mMax];
|
||||||
|
const char *mMemName[mMax];
|
||||||
|
MemStats();
|
||||||
|
void memChkNew(MemoryNames memIndex, const char *id);
|
||||||
|
void memChkDel(MemoryNames memIndex, const char *id);
|
||||||
|
void text(std::ostream &os);
|
||||||
|
// We would prefer to use an unordered_map, but that requires special compile switches.
|
||||||
|
// What a super great language.
|
||||||
|
typedef std::map<std::string,Int_z> MemMapType;
|
||||||
|
MemMapType mMemMap;
|
||||||
|
};
|
||||||
|
extern struct MemStats gMemStats;
|
||||||
|
extern int gMemLeakDebug;
|
||||||
|
|
||||||
|
// This is a memory leak detector.
|
||||||
|
// Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors,
|
||||||
|
// or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class
|
||||||
|
// as an ancestor to the class to be memory leak checked.
|
||||||
|
|
||||||
|
struct MemLabel {
|
||||||
|
std::string mccKey;
|
||||||
|
virtual ~MemLabel() {
|
||||||
|
Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if RN_DISABLE_MEMORY_LEAK_TEST
|
||||||
|
#define RN_MEMCHKNEW(type)
|
||||||
|
#define RN_MEMCHKDEL(type)
|
||||||
|
#define RN_MEMLOG(type,ptr)
|
||||||
|
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
|
||||||
|
struct checkerClass {};
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); }
|
||||||
|
#define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); }
|
||||||
|
|
||||||
|
#define RN_MEMLOG(type,ptr) { \
|
||||||
|
static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \
|
||||||
|
(ptr)->/* MemCheck##type:: */ mccKey = key; \
|
||||||
|
Utils::gMemStats.mMemMap[key]++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The above assumes that checkclass is MemCheck ## subClass
|
||||||
|
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
|
||||||
|
struct checkerClass : public virtual Utils::MemLabel { \
|
||||||
|
checkerClass() { RN_MEMCHKNEW(subClass); } \
|
||||||
|
virtual ~checkerClass() { \
|
||||||
|
RN_MEMCHKDEL(subClass); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace Utils
|
||||||
|
|
||||||
|
#endif
|
||||||
136
CommonLibs/ScalarTypes.h
Normal file
136
CommonLibs/ScalarTypes.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Range Networks, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This software is distributed under multiple licenses;
|
||||||
|
* see the COPYING file in the main directory for licensing
|
||||||
|
* information for this specific distribuion.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCALARTYPES_H
|
||||||
|
#define SCALARTYPES_H
|
||||||
|
#include <iostream> // For size_t
|
||||||
|
#include <stdint.h>
|
||||||
|
//#include "GSMCommon.h" // Was included for Z100Timer
|
||||||
|
|
||||||
|
// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
|
||||||
|
#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
|
||||||
|
Classname() : value(Init) {} \
|
||||||
|
Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
|
||||||
|
operator Basetype(void) const { return value; } /* Converts from basetype. */ \
|
||||||
|
Basetype operator=(Basetype wvalue) { return value = wvalue; } \
|
||||||
|
Basetype* operator&() { return &value; }
|
||||||
|
|
||||||
|
#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
|
||||||
|
Basetype operator++() { return ++value; } \
|
||||||
|
Basetype operator++(int) { return value++; } \
|
||||||
|
Basetype operator--() { return --value; } \
|
||||||
|
Basetype operator--(int) { return value--; } \
|
||||||
|
Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
|
||||||
|
Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
|
||||||
|
|
||||||
|
#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
|
||||||
|
_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
|
||||||
|
_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
|
||||||
|
|
||||||
|
|
||||||
|
#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
|
||||||
|
template <Basetype Init> \
|
||||||
|
struct Classname_i { \
|
||||||
|
Basetype value; \
|
||||||
|
_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
|
||||||
|
}; \
|
||||||
|
typedef Classname_i<0> Classname_z;
|
||||||
|
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// Where 'classname' is one of the types listed below, then:
|
||||||
|
// classname_z specifies a zero initialized type;
|
||||||
|
// classname_i<value> initializes the type to the specified value.
|
||||||
|
// We also define Float_z.
|
||||||
|
_DECLARE_SCALAR_TYPE(Int_i, Int_z, int)
|
||||||
|
_DECLARE_SCALAR_TYPE(Char_i, Char_z, signed char)
|
||||||
|
_DECLARE_SCALAR_TYPE(Int16_i, Int16_z, int16_t)
|
||||||
|
_DECLARE_SCALAR_TYPE(Int32_i, Int32_z, int32_t)
|
||||||
|
_DECLARE_SCALAR_TYPE(UInt_i, UInt_z, unsigned)
|
||||||
|
_DECLARE_SCALAR_TYPE(UChar_i, UChar_z, unsigned char)
|
||||||
|
_DECLARE_SCALAR_TYPE(UInt16_i, UInt16_z, uint16_t)
|
||||||
|
_DECLARE_SCALAR_TYPE(UInt32_i, UInt32_z, uint32_t)
|
||||||
|
_DECLARE_SCALAR_TYPE(Size_t_i, Size_t_z, size_t)
|
||||||
|
|
||||||
|
// Bool is special because it cannot accept some arithmetic funcs
|
||||||
|
//_DECLARE_SCALAR_TYPE(Bool_i, Bool_z, bool)
|
||||||
|
template <bool Init>
|
||||||
|
struct Bool_i {
|
||||||
|
bool value;
|
||||||
|
_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
|
||||||
|
};
|
||||||
|
typedef Bool_i<0> Bool_z;
|
||||||
|
|
||||||
|
// float is special, because C++ does not permit the template initalization:
|
||||||
|
struct Float_z {
|
||||||
|
float value;
|
||||||
|
_INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
|
||||||
|
};
|
||||||
|
struct Double_z {
|
||||||
|
double value;
|
||||||
|
_INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ItemWithValueAndWidth {
|
||||||
|
public:
|
||||||
|
virtual unsigned getValue() const = 0;
|
||||||
|
virtual unsigned getWidth() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A Range Networks Field with a specified width.
|
||||||
|
// See RLCMessages.h for examples.
|
||||||
|
template <int Width=32, unsigned Init=0>
|
||||||
|
class Field_i : public ItemWithValueAndWidth
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unsigned value;
|
||||||
|
_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
|
||||||
|
unsigned getWidth() const { return Width; }
|
||||||
|
unsigned getValue() const { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Synonym for Field_i, but no way to do it.
|
||||||
|
template <int Width, unsigned Init=0>
|
||||||
|
class Field_z : public ItemWithValueAndWidth
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unsigned value;
|
||||||
|
_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
|
||||||
|
unsigned getWidth() const { return Width; }
|
||||||
|
unsigned getValue() const { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is an uninitialized field.
|
||||||
|
template <int Width=32, unsigned Init=0>
|
||||||
|
class Field : public ItemWithValueAndWidth
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unsigned value;
|
||||||
|
_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
|
||||||
|
unsigned getWidth() const { return Width; }
|
||||||
|
unsigned getValue() const { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A Z100Timer with an initial value specified.
|
||||||
|
//template <int Init>
|
||||||
|
//class Z100Timer_i : public GSM::Z100Timer {
|
||||||
|
// public:
|
||||||
|
// Z100Timer_i() : GSM::Z100Timer(Init) {}
|
||||||
|
//};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -41,6 +41,10 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
|
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
|
||||||
{
|
{
|
||||||
assert(address);
|
assert(address);
|
||||||
@@ -255,6 +259,11 @@ void UDPSocket::open(unsigned short localPort)
|
|||||||
throw SocketError();
|
throw SocketError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes.
|
||||||
|
int on = 1;
|
||||||
|
setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||||
|
|
||||||
|
|
||||||
// bind
|
// bind
|
||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
size_t length = sizeof(address);
|
size_t length = sizeof(address);
|
||||||
|
|||||||
@@ -107,8 +107,9 @@ void Thread::start(void *(*task)(void*), void *arg)
|
|||||||
{
|
{
|
||||||
assert(mThread==((pthread_t)0));
|
assert(mThread==((pthread_t)0));
|
||||||
bool res;
|
bool res;
|
||||||
res = pthread_attr_init(&mAttrib);
|
// (pat) Moved initialization to constructor to avoid crash in destructor.
|
||||||
assert(!res);
|
//res = pthread_attr_init(&mAttrib);
|
||||||
|
//assert(!res);
|
||||||
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
|
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
|
||||||
assert(!res);
|
assert(!res);
|
||||||
res = pthread_create(&mThread, &mAttrib, task, arg);
|
res = pthread_create(&mThread, &mAttrib, task, arg);
|
||||||
|
|||||||
@@ -155,12 +155,16 @@ class Thread {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** Create a thread in a non-running state. */
|
/** Create a thread in a non-running state. */
|
||||||
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) { mStackSize=wStackSize;}
|
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
|
||||||
|
pthread_attr_init(&mAttrib); // (pat) moved this here.
|
||||||
|
mStackSize=wStackSize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Destroy the Thread.
|
Destroy the Thread.
|
||||||
It should be stopped and joined.
|
It should be stopped and joined.
|
||||||
*/
|
*/
|
||||||
|
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
|
||||||
~Thread() { pthread_attr_destroy(&mAttrib); }
|
~Thread() { pthread_attr_destroy(&mAttrib); }
|
||||||
|
|
||||||
|
|
||||||
@@ -168,7 +172,7 @@ class Thread {
|
|||||||
void start(void *(*task)(void*), void *arg);
|
void start(void *(*task)(void*), void *arg);
|
||||||
|
|
||||||
/** Join a thread that will stop on its own. */
|
/** Join a thread that will stop on its own. */
|
||||||
void join() { int s = pthread_join(mThread,NULL); assert(!s); }
|
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** A wrapper on usleep to sleep for milliseconds. */
|
/** A wrapper on usleep to sleep for milliseconds. */
|
||||||
inline void msleep(long v) { usleep(v*1000); }
|
inline void msleep(long v) { usleep(v*1000); }
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,4 @@
|
|||||||
/*
|
/* Copyright 2011, Range Networks, Inc. */
|
||||||
* Copyright 2011 Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <URLEncode.h>
|
#include <URLEncode.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|||||||
17
CommonLibs/URLEncodeTest.cpp
Normal file
17
CommonLibs/URLEncodeTest.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
#include "URLEncode.h"
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__);
|
||||||
|
cout << test << endl;
|
||||||
|
cout << URLEncode(test) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
211
CommonLibs/Utils.cpp
Normal file
211
CommonLibs/Utils.cpp
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Range Networks, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This software is distributed under multiple licenses;
|
||||||
|
* see the COPYING file in the main directory for licensing
|
||||||
|
* information for this specific distribuion.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h> // For usleep
|
||||||
|
#include <sys/time.h> // For gettimeofday
|
||||||
|
#include <stdio.h> // For vsnprintf
|
||||||
|
#include <ostream> // For ostream
|
||||||
|
#include <sstream> // For ostringstream
|
||||||
|
#include <string.h> // For strcpy
|
||||||
|
//#include "GSMCommon.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "MemoryLeak.h"
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
MemStats gMemStats;
|
||||||
|
int gMemLeakDebug = 0;
|
||||||
|
|
||||||
|
MemStats::MemStats()
|
||||||
|
{
|
||||||
|
memset(mMemNow,0,sizeof(mMemNow));
|
||||||
|
memset(mMemTotal,0,sizeof(mMemTotal));
|
||||||
|
memset(mMemName,0,sizeof(mMemName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemStats::text(std::ostream &os)
|
||||||
|
{
|
||||||
|
os << "Structs current total:\n";
|
||||||
|
for (int i = 0; i < mMax; i++) {
|
||||||
|
os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemStats::memChkNew(MemoryNames memIndex, const char *id)
|
||||||
|
{
|
||||||
|
/*std::cout << "new " #type "\n";*/
|
||||||
|
mMemNow[memIndex]++;
|
||||||
|
mMemTotal[memIndex]++;
|
||||||
|
mMemName[memIndex] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemStats::memChkDel(MemoryNames memIndex, const char *id)
|
||||||
|
{
|
||||||
|
/*std::cout << "del " #type "\n";*/
|
||||||
|
mMemNow[memIndex]--;
|
||||||
|
if (mMemNow[memIndex] < 0) {
|
||||||
|
LOG(ERR) << "Memory underflow on type "<<id;
|
||||||
|
if (gMemLeakDebug) assert(0);
|
||||||
|
mMemNow[memIndex] += 100; // Prevent another message for a while.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
|
||||||
|
{
|
||||||
|
return os << ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &osprintf(std::ostream &os, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char buf[300];
|
||||||
|
va_start(ap,fmt);
|
||||||
|
int n = vsnprintf(buf,300,fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||||
|
os << buf;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string format(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char buf[300];
|
||||||
|
va_start(ap,fmt);
|
||||||
|
int n = vsnprintf(buf,300,fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return time in seconds with high resolution.
|
||||||
|
// Note: In the past I found this to be a surprisingly expensive system call in linux.
|
||||||
|
double timef()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv,NULL);
|
||||||
|
return tv.tv_usec / 1000000.0 + tv.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string timestr()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
struct tm tm;
|
||||||
|
gettimeofday(&tv,NULL);
|
||||||
|
localtime_r(&tv.tv_sec,&tm);
|
||||||
|
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
|
||||||
|
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// High resolution sleep for the specified time.
|
||||||
|
// Return FALSE if time is already past.
|
||||||
|
void sleepf(double howlong)
|
||||||
|
{
|
||||||
|
if (howlong <= 0.00001) return; // Less than 10 usecs, forget it.
|
||||||
|
usleep((useconds_t) (1000000.0 * howlong));
|
||||||
|
}
|
||||||
|
|
||||||
|
//bool sleepuntil(double untilwhen)
|
||||||
|
//{
|
||||||
|
//double now = timef();
|
||||||
|
//double howlong = untilwhen - now; // Fractional time in seconds.
|
||||||
|
// We are not worrying about overflow because all times should be in the near future.
|
||||||
|
//if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it.
|
||||||
|
//sleepf(sleeptime);
|
||||||
|
//}
|
||||||
|
|
||||||
|
std::string Text2Str::str() const
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
text(ss);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Text2Str *val)
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
if (val) {
|
||||||
|
val->text(ss);
|
||||||
|
os << ss.str();
|
||||||
|
} else {
|
||||||
|
os << "(null)";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greatest Common Denominator.
|
||||||
|
// This is by Doug Brown.
|
||||||
|
int gcd(int x, int y)
|
||||||
|
{
|
||||||
|
if (x > y) {
|
||||||
|
return x % y == 0 ? y : gcd(y, x % y);
|
||||||
|
} else {
|
||||||
|
return y % x == 0 ? x : gcd(x, y % x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Split a C string into an argc,argv array in place; the input string is modified.
|
||||||
|
// Returns argc, and places results in argv, up to maxargc elements.
|
||||||
|
// The final argv receives the rest of the input string from maxargc on,
|
||||||
|
// even if it contains additional splitchars.
|
||||||
|
// The correct idiom for use is to make a copy of your string, like this:
|
||||||
|
// char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str());
|
||||||
|
// char *argv[2];
|
||||||
|
// int argc = cstrSplit(copy,argv,2,NULL);
|
||||||
|
// If you want to detect the error of too many arguments, add 1 to argv, like this:
|
||||||
|
// char *argv[3];
|
||||||
|
// int argc = cstrSplit(copy,argv,3,NULL);
|
||||||
|
// if (argc == 3) { error("too many arguments"; }
|
||||||
|
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars)
|
||||||
|
{
|
||||||
|
if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space.
|
||||||
|
int argc = 0;
|
||||||
|
while (argc < maxargc) {
|
||||||
|
while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars
|
||||||
|
if (! *in) return argc; // return if finished.
|
||||||
|
pargv[argc++] = in; // save ptr to start of arg.
|
||||||
|
in = strpbrk(in,splitchars); // go to end of arg.
|
||||||
|
if (!in) return argc; // return if finished.
|
||||||
|
*in++ = 0; // zero terminate this arg.
|
||||||
|
}
|
||||||
|
return argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; }
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; }
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; }
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; }
|
||||||
|
|
||||||
|
std::string replaceAll(const std::string input, const std::string search, const std::string replace)
|
||||||
|
{
|
||||||
|
std::string output = input;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
index = output.find(search, index);
|
||||||
|
if (index == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.replace(index, replace.length(), replace);
|
||||||
|
index += replace.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
148
CommonLibs/Utils.h
Normal file
148
CommonLibs/Utils.h
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Range Networks, Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This software is distributed under multiple licenses;
|
||||||
|
* see the COPYING file in the main directory for licensing
|
||||||
|
* information for this specific distribuion.
|
||||||
|
*
|
||||||
|
* This use of this software may be subject to additional restrictions.
|
||||||
|
* See the LEGAL file in the main directory for details.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GPRSUTILS_H
|
||||||
|
#define GPRSUTILS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h> // for sqrtf
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
extern double timef(); // high resolution time
|
||||||
|
extern const std::string timestr(); // A timestamp to print in messages.
|
||||||
|
extern void sleepf(double howlong); // high resolution sleep
|
||||||
|
extern int gcd(int x, int y);
|
||||||
|
|
||||||
|
// It is irritating to create a string just to interface to the brain-damaged
|
||||||
|
// C++ stream class, but this is only used for debug messages.
|
||||||
|
std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
|
||||||
|
|
||||||
|
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
|
||||||
|
|
||||||
|
// For classes with a text() function, provide a function to return a String,
|
||||||
|
// and also a standard << stream function that takes a pointer to the object.
|
||||||
|
// We dont provide the function that takes a reference to the object
|
||||||
|
// because it is too highly overloaded and generally doesnt work.
|
||||||
|
class Text2Str {
|
||||||
|
public:
|
||||||
|
virtual void text(std::ostream &os) const = 0;
|
||||||
|
std::string str() const;
|
||||||
|
};
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Text2Str *val);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Generic Activity Timer. Lots of controls to make everybody happy.
|
||||||
|
class ATimer {
|
||||||
|
double mStart;
|
||||||
|
//bool mActive;
|
||||||
|
double mLimitTime;
|
||||||
|
public:
|
||||||
|
ATimer() : mStart(0), mLimitTime(0) { }
|
||||||
|
ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
|
||||||
|
void start() { mStart=timef(); }
|
||||||
|
void stop() { mStart=0; }
|
||||||
|
bool active() { return !!mStart; }
|
||||||
|
double elapsed() { return timef() - mStart; }
|
||||||
|
bool expired() { return elapsed() > mLimitTime; }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct BitSet {
|
||||||
|
unsigned mBits;
|
||||||
|
void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
|
||||||
|
void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
|
||||||
|
unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
||||||
|
bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
|
||||||
|
unsigned bits() const { return mBits; }
|
||||||
|
operator int(void) const { return mBits; }
|
||||||
|
BitSet() { mBits = 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store current, min, max and compute running average and standard deviation.
|
||||||
|
template<class Type> struct Statistic {
|
||||||
|
Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
|
||||||
|
unsigned mCnt;
|
||||||
|
double mSum;
|
||||||
|
//double mSum2; // sum of squares.
|
||||||
|
// (Type) cast needed in case Type is an enum, stupid language.
|
||||||
|
Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
|
||||||
|
// Set the current value and add a statisical point.
|
||||||
|
void addPoint(Type val) {
|
||||||
|
mCurrent = val;
|
||||||
|
if (mCnt == 0 || val < mMin) {mMin = val;}
|
||||||
|
if (mCnt == 0 || val > mMax) {mMax = val;}
|
||||||
|
mCnt++;
|
||||||
|
mSum += val;
|
||||||
|
//mSum2 += val * val;
|
||||||
|
}
|
||||||
|
Type getCurrent() const { // Return current value.
|
||||||
|
return mCnt ? mCurrent : 0;
|
||||||
|
}
|
||||||
|
double getAvg() const { // Return average.
|
||||||
|
return mCnt==0 ? 0 : mSum/mCnt;
|
||||||
|
};
|
||||||
|
//float getSD() const { // Return standard deviation. Use low precision square root function.
|
||||||
|
// return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
|
||||||
|
//}
|
||||||
|
|
||||||
|
void text(std::ostream &os) const { // Print everything in parens.
|
||||||
|
os << "("<<mCurrent;
|
||||||
|
if (mMin != mMax) { // Not point in printing all this stuff if min == max.
|
||||||
|
os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
|
||||||
|
if (mCnt <= 999999) {
|
||||||
|
os <<LOGVAR2("N",mCnt);
|
||||||
|
} else { // Shorten this up:
|
||||||
|
char buf[10], *ep;
|
||||||
|
sprintf(buf,"%.3g",round(mCnt));
|
||||||
|
if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
|
||||||
|
os << LOGVAR2("N",buf);
|
||||||
|
}
|
||||||
|
// os<<LOGVAR2("sd",getSD()) standard deviation not interesting
|
||||||
|
}
|
||||||
|
os << ")";
|
||||||
|
// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
|
||||||
|
}
|
||||||
|
// Not sure if this works:
|
||||||
|
//std::string statStr() const {
|
||||||
|
// return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
|
||||||
|
//}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This I/O mechanism is so dumb:
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
|
||||||
|
|
||||||
|
|
||||||
|
// Yes, they botched and left this out:
|
||||||
|
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
|
||||||
|
|
||||||
|
std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
|
||||||
|
|
||||||
|
std::string replaceAll(const std::string input, const std::string search, const std::string replace);
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -32,6 +32,10 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
// We cant use Logger.h in this file...
|
||||||
|
extern int gVectorDebug;
|
||||||
|
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,6 +63,14 @@ template <class T> class Vector {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/****
|
||||||
|
char *inspect() {
|
||||||
|
static char buf[100];
|
||||||
|
sprintf(buf," mData=%p mStart=%p mEnd=%p ",mData,mStart,mEnd);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
***/
|
||||||
|
|
||||||
/** Return the size of the Vector. */
|
/** Return the size of the Vector. */
|
||||||
size_t size() const
|
size_t size() const
|
||||||
{
|
{
|
||||||
@@ -246,6 +258,7 @@ template <class T> class Vector {
|
|||||||
T* begin() { return mStart; }
|
T* begin() { return mStart; }
|
||||||
const T* end() const { return mEnd; }
|
const T* end() const { return mEnd; }
|
||||||
T* end() { return mEnd; }
|
T* end() { return mEnd; }
|
||||||
|
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
#include "Vector.h"
|
#include "Vector.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// We must have a gConfig now to include Vector.
|
||||||
|
#include "Configuration.h"
|
||||||
|
ConfigurationTable gConfig;
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef Vector<int> TestVector;
|
typedef Vector<int> TestVector;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ L3Message* Control::getMessage(LogicalChannel *LCH, unsigned SAPI)
|
|||||||
// But if they do, we should ignore them.
|
// But if they do, we should ignore them.
|
||||||
// They should not send more than one in any case, but we need to be
|
// They should not send more than one in any case, but we need to be
|
||||||
// ready for whatever crazy behavior they throw at us.
|
// ready for whatever crazy behavior they throw at us.
|
||||||
unsigned count = gConfig.getNum("GSM.Control.GPRSMaxIgnore",5);
|
unsigned count = gConfig.getNum("GSM.Control.GPRSMaxIgnore");
|
||||||
while (count && dynamic_cast<const GSM::L3GPRSSuspensionRequest*>(msg)) {
|
while (count && dynamic_cast<const GSM::L3GPRSSuspensionRequest*>(msg)) {
|
||||||
LOG(NOTICE) << "ignoring GPRS suspension request";
|
LOG(NOTICE) << "ignoring GPRS suspension request";
|
||||||
msg = getMessageCore(LCH,SAPI);
|
msg = getMessageCore(LCH,SAPI);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ string getConfig()
|
|||||||
};
|
};
|
||||||
string config = "";
|
string config = "";
|
||||||
for (const char **p = configs; *p; p++) {
|
for (const char **p = configs; *p; p++) {
|
||||||
string configValue = gConfig.getStr(*p, "");
|
string configValue = gConfig.getStr(*p);
|
||||||
if (configValue.length() == 0) return "";
|
if (configValue.length() == 0) return "";
|
||||||
config.append("&");
|
config.append("&");
|
||||||
config.append(*p);
|
config.append(*p);
|
||||||
@@ -88,7 +88,7 @@ string getConfig()
|
|||||||
RRLPServer::RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH)
|
RRLPServer::RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH)
|
||||||
{
|
{
|
||||||
trouble = false;
|
trouble = false;
|
||||||
url = gConfig.getStr("GSM.RRLP.SERVER.URL", "");
|
url = gConfig.getStr("GSM.RRLP.SERVER.URL");
|
||||||
if (url.length() == 0) {
|
if (url.length() == 0) {
|
||||||
LOG(INFO) << "RRLP not configured";
|
LOG(INFO) << "RRLP not configured";
|
||||||
trouble = true;
|
trouble = true;
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ void SIPInterface::write(const struct sockaddr_in* dest, osip_message_t *msg)
|
|||||||
LOG(INFO) << "write " << firstLine;
|
LOG(INFO) << "write " << firstLine;
|
||||||
LOG(DEBUG) << "write " << str;
|
LOG(DEBUG) << "write " << str;
|
||||||
|
|
||||||
if (random()%100 < gConfig.getNum("Test.SIP.SimulatedPacketLoss",0)) {
|
if (random()%100 < gConfig.getNum("Test.SIP.SimulatedPacketLoss")) {
|
||||||
LOG(NOTICE) << "simulating dropped outbound SIP packet: " << firstLine;
|
LOG(NOTICE) << "simulating dropped outbound SIP packet: " << firstLine;
|
||||||
free(str);
|
free(str);
|
||||||
return;
|
return;
|
||||||
@@ -237,7 +237,7 @@ void SIPInterface::drive()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mReadBuffer[numRead] = '\0';
|
mReadBuffer[numRead] = '\0';
|
||||||
if (random()%100 < gConfig.getNum("Test.SIP.SimulatedPacketLoss",0)) {
|
if (random()%100 < gConfig.getNum("Test.SIP.SimulatedPacketLoss")) {
|
||||||
LOG(NOTICE) << "simulating dropped inbound SIP packet: " << mReadBuffer;
|
LOG(NOTICE) << "simulating dropped inbound SIP packet: " << mReadBuffer;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ void* ClockLoopAdapter(TransceiverManager *transceiver)
|
|||||||
void TransceiverManager::clockHandler()
|
void TransceiverManager::clockHandler()
|
||||||
{
|
{
|
||||||
char buffer[MAX_UDP_LENGTH];
|
char buffer[MAX_UDP_LENGTH];
|
||||||
int msgLen = mClockSocket.read(buffer,gConfig.getNum("TRX.Timeout.Clock",10)*1000);
|
int msgLen = mClockSocket.read(buffer,gConfig.getNum("TRX.Timeout.Clock")*1000);
|
||||||
|
|
||||||
// Did the transceiver die??
|
// Did the transceiver die??
|
||||||
if (msgLen<0) {
|
if (msgLen<0) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
|
|||||||
|
|
||||||
// Set up the performance reporter.
|
// Set up the performance reporter.
|
||||||
#include <Reporting.h>
|
#include <Reporting.h>
|
||||||
ReportingTable gReports(gConfig.getStr("Control.Reporting.StatsTable","/var/log/OpenBTSStats.db").c_str());
|
ReportingTable gReports(gConfig.getStr("Control.Reporting.StatsTable").c_str());
|
||||||
|
|
||||||
#include <TRXManager.h>
|
#include <TRXManager.h>
|
||||||
#include <GSML1FEC.h>
|
#include <GSML1FEC.h>
|
||||||
@@ -306,7 +306,7 @@ int main(int argc, char *argv[])
|
|||||||
srandom(time(NULL));
|
srandom(time(NULL));
|
||||||
|
|
||||||
gConfig.setUpdateHook(purgeConfig);
|
gConfig.setUpdateHook(purgeConfig);
|
||||||
gLogInit("openbts",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7);
|
gLogInit("openbts",gConfig.getStr("Log.Level").c_str());
|
||||||
LOG(ALERT) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__;
|
LOG(ALERT) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__;
|
||||||
|
|
||||||
COUT("\n\n" << gOpenBTSWelcome << "\n");
|
COUT("\n\n" << gOpenBTSWelcome << "\n");
|
||||||
@@ -488,7 +488,7 @@ int main(int argc, char *argv[])
|
|||||||
gBTS.addPCH(&CCCH2);
|
gBTS.addPCH(&CCCH2);
|
||||||
|
|
||||||
// Be sure we are not over-reserving.
|
// Be sure we are not over-reserving.
|
||||||
if (gConfig.getNum("GSM.Channels.SDCCHReserve",0)>=(int)gBTS.SDCCHTotal()) {
|
if (gConfig.getNum("GSM.Channels.SDCCHReserve")>=(int)gBTS.SDCCHTotal()) {
|
||||||
unsigned val = gBTS.SDCCHTotal() - 1;
|
unsigned val = gBTS.SDCCHTotal() - 1;
|
||||||
LOG(CRIT) << "GSM.Channels.SDCCHReserve too big, changing to " << val;
|
LOG(CRIT) << "GSM.Channels.SDCCHReserve too big, changing to " << val;
|
||||||
gConfig.set("GSM.Channels.SDCCHReserve",val);
|
gConfig.set("GSM.Channels.SDCCHReserve",val);
|
||||||
@@ -505,7 +505,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
struct sockaddr_un cmdSockName;
|
struct sockaddr_un cmdSockName;
|
||||||
cmdSockName.sun_family = AF_UNIX;
|
cmdSockName.sun_family = AF_UNIX;
|
||||||
const char* sockpath = gConfig.getStr("CLI.SocketPath","/var/run/OpenBTS/command").c_str();
|
const char* sockpath = gConfig.getStr("CLI.SocketPath").c_str();
|
||||||
char rmcmd[strlen(sockpath)+5];
|
char rmcmd[strlen(sockpath)+5];
|
||||||
sprintf(rmcmd,"rm %s",sockpath);
|
sprintf(rmcmd,"rm %s",sockpath);
|
||||||
system(rmcmd);
|
system(rmcmd);
|
||||||
|
|||||||
Reference in New Issue
Block a user