diff --git a/A51.cpp b/A51.cpp
new file mode 100644
index 0000000..1e9098d
--- /dev/null
+++ b/A51.cpp
@@ -0,0 +1,228 @@
+/*
+ * A pedagogical implementation of A5/1.
+ *
+ * Copyright (C) 1998-1999: Marc Briceno, Ian Goldberg, and David Wagner
+ *
+ * The source code below is optimized for instructional value and clarity.
+ * Performance will be terrible, but that's not the point.
+ * The algorithm is written in the C programming language to avoid ambiguities
+ * inherent to the English language. Complain to the 9th Circuit of Appeals
+ * if you have a problem with that.
+ *
+ * This software may be export-controlled by US law.
+ *
+ * This software is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.
+ * Copyright remains the authors' and as such any Copyright notices in
+ * the code are not to be removed.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The license and distribution terms for any publicly available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution license
+ * [including the GNU Public License.]
+ *
+ * Background: The Global System for Mobile communications is the most widely
+ * deployed cellular telephony system in the world. GSM makes use of
+ * four core cryptographic algorithms, neither of which has been published by
+ * the GSM MOU. This failure to subject the algorithms to public review is all
+ * the more puzzling given that over 100 million GSM
+ * subscribers are expected to rely on the claimed security of the system.
+ *
+ * The four core GSM algorithms are:
+ * A3 authentication algorithm
+ * A5/1 "strong" over-the-air voice-privacy algorithm
+ * A5/2 "weak" over-the-air voice-privacy algorithm
+ * A8 voice-privacy key generation algorithm
+ *
+ * In April of 1998, our group showed that COMP128, the algorithm used by the
+ * overwhelming majority of GSM providers for both A3 and A8
+ * functionality was fatally flawed and allowed for cloning of GSM mobile
+ * phones.
+ * Furthermore, we demonstrated that all A8 implementations we could locate,
+ * including the few that did not use COMP128 for key generation, had been
+ * deliberately weakened by reducing the keyspace from 64 bits to 54 bits.
+ * The remaining 10 bits are simply set to zero!
+ *
+ * See http://www.scard.org/gsm for additional information.
+ *
+ * The question so far unanswered is if A5/1, the "stronger" of the two
+ * widely deployed voice-privacy algorithm is at least as strong as the
+ * key. Meaning: "Does A5/1 have a work factor of at least 54 bits"?
+ * Absent a publicly available A5/1 reference implementation, this question
+ * could not be answered. We hope that our reference implementation below,
+ * which has been verified against official A5/1 test vectors, will provide
+ * the cryptographic community with the base on which to construct the
+ * answer to this important question.
+ *
+ * Initial indications about the strength of A5/1 are not encouraging.
+ * A variant of A5, while not A5/1 itself, has been estimated to have a
+ * work factor of well below 54 bits. See http://jya.com/crack-a5.htm for
+ * background information and references.
+ *
+ * With COMP128 broken and A5/1 published below, we will now turn our attention
+ * to A5/2. The latter has been acknowledged by the GSM community to have
+ * been specifically designed by intelligence agencies for lack of security.
+ *
+ */
+
+
+#include
+#include
+#include "A51.h"
+
+/* Masks for the three shift registers */
+#define R1MASK 0x07FFFF /* 19 bits, numbered 0..18 */
+#define R2MASK 0x3FFFFF /* 22 bits, numbered 0..21 */
+#define R3MASK 0x7FFFFF /* 23 bits, numbered 0..22 */
+
+/* Middle bit of each of the three shift registers, for clock control */
+#define R1MID 0x000100 /* bit 8 */
+#define R2MID 0x000400 /* bit 10 */
+#define R3MID 0x000400 /* bit 10 */
+
+/* The three shift registers. They're in global variables to make the code
+ * easier to understand.
+ * A better implementation would not use global variables. */
+word R1, R2, R3;
+
+/* Look at the middle bits of R1,R2,R3, take a vote, and
+ * return the majority value of those 3 bits. */
+bit majority() {
+ int sum;
+ sum = ((R1>>8)&1) + ((R2>>10)&1) + ((R3>>10)&1);
+ if (sum >= 2)
+ return 1;
+ else
+ return 0;
+}
+
+/* Clock two or three of R1,R2,R3, with clock control
+ * according to their middle bits.
+ * Specifically, we clock Ri whenever Ri's middle bit
+ * agrees with the majority value of the three middle bits.*/
+void clock() {
+ bit maj = majority();
+ if (((R1&R1MID)!=0) == maj)
+ R1 = ((R1<<1) & R1MASK) | (1 & (R1>>18 ^ R1>>17 ^ R1>>16 ^ R1>>13));
+ if (((R2&R2MID)!=0) == maj)
+ R2 = ((R2<<1) & R2MASK) | (1 & (R2>>21 ^ R2>>20));
+ if (((R3&R3MID)!=0) == maj)
+ R3 = ((R3<<1) & R3MASK) | (1 & (R3>>22 ^ R3>>21 ^ R3>>20 ^ R3>>7));
+}
+
+/* Clock all three of R1,R2,R3, ignoring their middle bits.
+ * This is only used for key setup. */
+void clockallthree() {
+ R1 = ((R1<<1) & R1MASK) | (1 & (R1>>18 ^ R1>>17 ^ R1>>16 ^ R1>>13));
+ R2 = ((R2<<1) & R2MASK) | (1 & (R2>>21 ^ R2>>20));
+ R3 = ((R3<<1) & R3MASK) | (1 & (R3>>22 ^ R3>>21 ^ R3>>20 ^ R3>>7));
+}
+
+/* Generate an output bit from the current state.
+ * You grab a bit from each register via the output generation taps;
+ * then you XOR the resulting three bits. */
+bit getbit() {
+ return ((R1>>18)^(R2>>21)^(R3>>22))&1;
+}
+
+/* Do the A5/1 key setup. This routine accepts a 64-bit key and
+ * a 22-bit frame number. */
+void keysetup(byte key[8], word frame) {
+ int i;
+ bit keybit, framebit;
+
+ /* Zero out the shift registers. */
+ R1 = R2 = R3 = 0;
+
+ /* Load the key into the shift registers,
+ * LSB of first byte of key array first,
+ * clocking each register once for every
+ * key bit loaded. (The usual clock
+ * control rule is temporarily disabled.) */
+ for (i=0; i<64; i++) {
+ clockallthree(); /* always clock */
+ keybit = (key[i/8] >> (i&7)) & 1; /* The i-th bit of the
+key */
+ R1 ^= keybit; R2 ^= keybit; R3 ^= keybit;
+ }
+
+ /* Load the frame number into the shift
+ * registers, LSB first,
+ * clocking each register once for every
+ * key bit loaded. (The usual clock
+ * control rule is still disabled.) */
+ for (i=0; i<22; i++) {
+ clockallthree(); /* always clock */
+ framebit = (frame >> i) & 1; /* The i-th bit of the frame #
+*/
+ R1 ^= framebit; R2 ^= framebit; R3 ^= framebit;
+ }
+
+ /* Run the shift registers for 100 clocks
+ * to mix the keying material and frame number
+ * together with output generation disabled,
+ * so that there is sufficient avalanche.
+ * We re-enable the majority-based clock control
+ * rule from now on. */
+ for (i=0; i<100; i++) {
+ clock();
+ }
+
+ /* Now the key is properly set up. */
+}
+
+/* Generate output. We generate 228 bits of
+ * keystream output. The first 114 bits is for
+ * the A->B frame; the next 114 bits is for the
+ * B->A frame. You allocate a 15-byte buffer
+ * for each direction, and this function fills
+ * it in. */
+void run(byte AtoBkeystream[], byte BtoAkeystream[]) {
+ int i;
+
+ /* Zero out the output buffers. */
+ for (i=0; i<=113/8; i++)
+ AtoBkeystream[i] = BtoAkeystream[i] = 0;
+
+ /* Generate 114 bits of keystream for the
+ * A->B direction. Store it, MSB first. */
+ for (i=0; i<114; i++) {
+ clock();
+ AtoBkeystream[i/8] |= getbit() << (7-(i&7));
+ }
+
+ /* Generate 114 bits of keystream for the
+ * B->A direction. Store it, MSB first. */
+ for (i=0; i<114; i++) {
+ clock();
+ BtoAkeystream[i/8] |= getbit() << (7-(i&7));
+ }
+}
+
+void A51_GSM( byte *key, int klen, int count, byte *block1, byte *block2 )
+{
+ assert(klen == 64);
+ keysetup(key, count); // TODO - frame and count are not the same
+ run(block1, block2);
+}
diff --git a/A51.h b/A51.h
new file mode 100644
index 0000000..d1337de
--- /dev/null
+++ b/A51.h
@@ -0,0 +1,11 @@
+
+
+#include
+#include
+
+typedef unsigned char byte;
+typedef unsigned long word;
+typedef word bit;
+
+void A51_GSM( byte *key, int klen, int count, byte *block1, byte *block2 );
+
diff --git a/A51Test.cpp b/A51Test.cpp
new file mode 100644
index 0000000..57ce467
--- /dev/null
+++ b/A51Test.cpp
@@ -0,0 +1,162 @@
+/*
+ * A pedagogical implementation of A5/1.
+ *
+ * Copyright (C) 1998-1999: Marc Briceno, Ian Goldberg, and David Wagner
+ *
+ * The source code below is optimized for instructional value and clarity.
+ * Performance will be terrible, but that's not the point.
+ * The algorithm is written in the C programming language to avoid ambiguities
+ * inherent to the English language. Complain to the 9th Circuit of Appeals
+ * if you have a problem with that.
+ *
+ * This software may be export-controlled by US law.
+ *
+ * This software is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.
+ * Copyright remains the authors' and as such any Copyright notices in
+ * the code are not to be removed.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The license and distribution terms for any publicly available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution license
+ * [including the GNU Public License.]
+ *
+ * Background: The Global System for Mobile communications is the most widely
+ * deployed cellular telephony system in the world. GSM makes use of
+ * four core cryptographic algorithms, neither of which has been published by
+ * the GSM MOU. This failure to subject the algorithms to public review is all
+ * the more puzzling given that over 100 million GSM
+ * subscribers are expected to rely on the claimed security of the system.
+ *
+ * The four core GSM algorithms are:
+ * A3 authentication algorithm
+ * A5/1 "strong" over-the-air voice-privacy algorithm
+ * A5/2 "weak" over-the-air voice-privacy algorithm
+ * A8 voice-privacy key generation algorithm
+ *
+ * In April of 1998, our group showed that COMP128, the algorithm used by the
+ * overwhelming majority of GSM providers for both A3 and A8
+ * functionality was fatally flawed and allowed for cloning of GSM mobile
+ * phones.
+ * Furthermore, we demonstrated that all A8 implementations we could locate,
+ * including the few that did not use COMP128 for key generation, had been
+ * deliberately weakened by reducing the keyspace from 64 bits to 54 bits.
+ * The remaining 10 bits are simply set to zero!
+ *
+ * See http://www.scard.org/gsm for additional information.
+ *
+ * The question so far unanswered is if A5/1, the "stronger" of the two
+ * widely deployed voice-privacy algorithm is at least as strong as the
+ * key. Meaning: "Does A5/1 have a work factor of at least 54 bits"?
+ * Absent a publicly available A5/1 reference implementation, this question
+ * could not be answered. We hope that our reference implementation below,
+ * which has been verified against official A5/1 test vectors, will provide
+ * the cryptographic community with the base on which to construct the
+ * answer to this important question.
+ *
+ * Initial indications about the strength of A5/1 are not encouraging.
+ * A variant of A5, while not A5/1 itself, has been estimated to have a
+ * work factor of well below 54 bits. See http://jya.com/crack-a5.htm for
+ * background information and references.
+ *
+ * With COMP128 broken and A5/1 published below, we will now turn our attention
+ * to A5/2. The latter has been acknowledged by the GSM community to have
+ * been specifically designed by intelligence agencies for lack of security.
+ *
+ */
+
+
+#include
+#include
+#include
+#include "./A51.h"
+
+
+/* Test the code by comparing it against
+ * a known-good test vector. */
+void test() {
+ byte key[8] = {0x12, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
+ word frame = 0x134;
+ byte goodAtoB[15] = { 0x53, 0x4E, 0xAA, 0x58, 0x2F, 0xE8, 0x15,
+ 0x1A, 0xB6, 0xE1, 0x85, 0x5A, 0x72, 0x8C, 0x00 };
+ byte goodBtoA[15] = { 0x24, 0xFD, 0x35, 0xA3, 0x5D, 0x5F, 0xB6,
+ 0x52, 0x6D, 0x32, 0xF9, 0x06, 0xDF, 0x1A, 0xC0 };
+ byte AtoB[15], BtoA[15];
+ int i, failed=0;
+
+ A51_GSM(key, 64, frame, AtoB, BtoA);
+
+ /* Compare against the test vector. */
+ for (i=0; i<15; i++)
+ if (AtoB[i] != goodAtoB[i])
+ failed = 1;
+ for (i=0; i<15; i++)
+ if (BtoA[i] != goodBtoA[i])
+ failed = 1;
+
+ /* Print some debugging output. */
+ printf("key: 0x");
+ for (i=0; i<8; i++)
+ printf("%02X", key[i]);
+ printf("\n");
+ printf("frame number: 0x%06X\n", (unsigned int)frame);
+ printf("known good output:\n");
+ printf(" A->B: 0x");
+ for (i=0; i<15; i++)
+ printf("%02X", goodAtoB[i]);
+ printf(" B->A: 0x");
+ for (i=0; i<15; i++)
+ printf("%02X", goodBtoA[i]);
+ printf("\n");
+ printf("observed output:\n");
+ printf(" A->B: 0x");
+ for (i=0; i<15; i++)
+ printf("%02X", AtoB[i]);
+ printf(" B->A: 0x");
+ for (i=0; i<15; i++)
+ printf("%02X", BtoA[i]);
+ printf("\n");
+
+ if (!failed) {
+ printf("Self-check succeeded: everything looks ok.\n");
+ } else {
+ /* Problems! The test vectors didn't compare*/
+ printf("\nI don't know why this broke; contact the authors.\n");
+ exit(1);
+ }
+
+ printf("time test\n");
+ int n = 10000;
+ float t = clock();
+ for (i = 0; i < n; i++) {
+ A51_GSM(key, 64, frame, AtoB, BtoA);
+ }
+ t = (clock() - t) / (CLOCKS_PER_SEC * (float)n);
+ printf("A51_GSM takes %g seconds per iteration\n", t);
+}
+
+int main(void) {
+ test();
+ return 0;
+}
diff --git a/BitVector.h b/BitVector.h
index e244be7..63c4e91 100644
--- a/BitVector.h
+++ b/BitVector.h
@@ -351,9 +351,12 @@ class BitVector : public Vector {
other.mData=NULL;
}
- void settfb(int i, int j) const
+ /** Set a bit */
+ void settfb(size_t index, int value)
{
- mStart[i] = j;
+ char *dp = mStart+index;
+ assert(dp {
/** Slice the whole signal into bits. */
BitVector sliced() const;
+ /** Return a soft bit. */
+ float softbit(size_t index) const
+ {
+ const float *dp = mStart+index;
+ assert(dp
#include
-#ifdef DEBUG_CONFIG
-#define debugLogEarly gLogEarly
-#else
-#define debugLogEarly
-#endif
-
using namespace std;
@@ -84,9 +78,24 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
if (!sqlite3_command(mDB,createConfigTable)) {
gLogEarly(LOG_EMERG, "cannot create configuration table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
+ // Set high-concurrency WAL mode.
+ if (!sqlite3_command(mDB,enableWAL)) {
+ gLogEarly(LOG_EMERG, "cannot enable WAL mode on database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
+ }
// Build CommonLibs schema
ConfigurationKey *tmp;
+ tmp = new ConfigurationKey("Control.NumSQLTries","3",
+ "attempts",
+ ConfigurationKey::DEVELOPER,
+ ConfigurationKey::VALRANGE,
+ "1:10",// educated guess
+ false,
+ "Number of times to retry SQL queries before declaring a database access failure."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ delete tmp;
+
tmp = new ConfigurationKey("Log.Alarms.Max","20",
"alarms",
ConfigurationKey::CUSTOMER,
@@ -96,7 +105,7 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
"Maximum number of alarms to remember inside the application."
);
mSchema[tmp->getName()] = *tmp;
- free(tmp);
+ delete tmp;
tmp = new ConfigurationKey("Log.File","",
"",
@@ -110,7 +119,7 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
"To disable again, execute \"unconfig Log.File\"."
);
mSchema[tmp->getName()] = *tmp;
- free(tmp);
+ delete tmp;
tmp = new ConfigurationKey("Log.Level","NOTICE",
"",
@@ -128,7 +137,7 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
"Default logging level when no other level is defined for a file."
);
mSchema[tmp->getName()] = *tmp;
- free(tmp);
+ delete tmp;
// Add application specific schema
mSchema.insert(wSchema.begin(), wSchema.end());
@@ -150,12 +159,13 @@ string ConfigurationTable::getDefaultSQL(const std::string& program, const std::
ss << "-- rather in the program's ConfigurationKey schema." << endl;
ss << "--" << endl;
ss << "PRAGMA foreign_keys=OFF;" << endl;
+ ss << "PRAGMA journal_mode=WAL;" << endl;
ss << "BEGIN TRANSACTION;" << endl;
- ss << "CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');" << endl;
+ ss << "CREATE TABLE IF NOT EXISTS CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');" << endl;
mp = mSchema.begin();
while (mp != mSchema.end()) {
- ss << "INSERT INTO \"CONFIG\" VALUES(";
+ ss << "INSERT OR IGNORE INTO \"CONFIG\" VALUES(";
// name
ss << "'" << mp->first << "',";
// default
@@ -269,7 +279,8 @@ bool ConfigurationTable::defines(const string& key)
ScopedLock lock(mLock);
return lookup(key).defined();
} catch (ConfigurationTableKeyNotFound) {
- debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
+ // TODO: re-enable once we figure out why this message is being sent to syslog regardless of log level
+ //gLogEarly(LOG_DEBUG, "configuration parameter %s not found", key.c_str());
return false;
}
}
@@ -580,7 +591,7 @@ string ConfigurationTable::getStr(const string& key)
return lookup(key).value();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ gLogEarly(LOG_DEBUG, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
@@ -592,7 +603,7 @@ bool ConfigurationTable::getBool(const string& key)
return getNum(key) != 0;
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ gLogEarly(LOG_DEBUG, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
@@ -606,7 +617,7 @@ long ConfigurationTable::getNum(const string& key)
return lookup(key).number();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ gLogEarly(LOG_DEBUG, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
@@ -619,7 +630,7 @@ float ConfigurationTable::getFloat(const string& key)
return lookup(key).floatNumber();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ gLogEarly(LOG_DEBUG, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
@@ -634,7 +645,7 @@ std::vector ConfigurationTable::getVectorOfStrings(const string& key)
line = strdup(rec.value().c_str());
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ gLogEarly(LOG_DEBUG, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
@@ -665,7 +676,7 @@ std::vector ConfigurationTable::getVector(const string& key)
line = strdup(rec.value().c_str());
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ gLogEarly(LOG_DEBUG, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
@@ -753,7 +764,13 @@ bool ConfigurationTable::set(const string& key, const string& value)
{
assert(mDB);
ScopedLock lock(mLock);
- string cmd = "INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)";
+ string cmd;
+ if (keyDefinedInSchema(key)) {
+ cmd = "INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL,COMMENTS) VALUES (\"" + key + "\",\"" + value + "\",1,\'" + mSchema[key].getDescription() + "\')";
+ } else {
+ cmd = "INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)";
+ }
+
bool success = sqlite3_command(mDB,cmd.c_str());
// Cache the result.
if (success) mCache[key] = ConfigurationRecord(value);
@@ -767,18 +784,6 @@ bool ConfigurationTable::set(const string& key, long value)
return set(key,buffer);
}
-
-bool ConfigurationTable::set(const string& key)
-{
- assert(mDB);
- ScopedLock lock(mLock);
- 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;
-}
-
-
void ConfigurationTable::checkCacheAge()
{
// mLock is set by caller
diff --git a/Configuration.h b/Configuration.h
index cd4838e..959cdf8 100644
--- a/Configuration.h
+++ b/Configuration.h
@@ -173,6 +173,7 @@ typedef std::map ConfigurationRecordMap;
typedef std::map ConfigurationMap;
class ConfigurationKey;
typedef std::map ConfigurationKeyMap;
+ConfigurationKeyMap getConfigurationKeys();
/**
A class for maintaining a configuration key-value table,
@@ -263,9 +264,6 @@ class ConfigurationTable {
/** Set or change a value in the table. */
bool set(const std::string& key, long value);
- /** Create an entry in the table, no value though. */
- bool set(const std::string& key);
-
/**
Remove an entry from the table.
Will not alter required values.
diff --git a/ConfigurationTest.cpp b/ConfigurationTest.cpp
index 2fd43e9..290db64 100644
--- a/ConfigurationTest.cpp
+++ b/ConfigurationTest.cpp
@@ -121,7 +121,7 @@ ConfigurationKeyMap getConfigurationKeys()
""
);
map[tmp->getName()] = *tmp;
- free(tmp);
+ delete tmp;
tmp = new ConfigurationKey("numnumber","42",
"",
@@ -132,7 +132,7 @@ ConfigurationKeyMap getConfigurationKeys()
""
);
map[tmp->getName()] = *tmp;
- free(tmp);
+ delete tmp;
tmp = new ConfigurationKey("newstring","new string value",
"",
@@ -143,7 +143,7 @@ ConfigurationKeyMap getConfigurationKeys()
""
);
map[tmp->getName()] = *tmp;
- free(tmp);
+ delete tmp;
return map;
}
diff --git a/Makefile.am b/Makefile.am
index 26a55ed..3c4e5bf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -41,7 +41,8 @@ libcommon_la_SOURCES = \
Configuration.cpp \
sqlite3util.cpp \
URLEncode.cpp \
- Utils.cpp
+ Utils.cpp \
+ A51.cpp
noinst_PROGRAMS = \
BitVectorTest \
@@ -53,7 +54,8 @@ noinst_PROGRAMS = \
ConfigurationTest \
LogTest \
URLEncodeTest \
- F16Test
+ F16Test \
+ A51Test
# ReportingTest
@@ -72,7 +74,8 @@ noinst_HEADERS = \
URLEncode.h \
Utils.h \
Logger.h \
- sqlite3util.h
+ sqlite3util.h \
+ A51.h
URLEncodeTest_SOURCES = URLEncodeTest.cpp
URLEncodeTest_LDADD = libcommon.la
@@ -108,6 +111,9 @@ LogTest_LDADD = libcommon.la $(SQLITE_LA)
F16Test_SOURCES = F16Test.cpp
+A51Test_SOURCES = A51Test.cpp
+A51Test_LDADD = libcommon.la
+
MOSTLYCLEANFILES += testSource testDestination
diff --git a/Reporting.cpp b/Reporting.cpp
index 3ea7eed..6782186 100644
--- a/Reporting.cpp
+++ b/Reporting.cpp
@@ -1,6 +1,6 @@
/**@file Module for performance-reporting mechanisms. */
/*
-* Copyright 2012 Range Networks, Inc.
+* Copyright 2012, 2013 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -53,11 +53,25 @@ ReportingTable::ReportingTable(const char* filename)
if (!sqlite3_command(mDB,createReportingTable)) {
gLogEarly(LOG_EMERG | mFacility, "cannot create reporting table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
+ // Set high-concurrency WAL mode.
+ if (!sqlite3_command(mDB,enableWAL)) {
+ gLogEarly(LOG_EMERG | mFacility, "Cannot enable WAL mode on database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
+ }
+ // Start the commit thread
+ mBatchCommitter.start((void*(*)(void*))reportingBatchCommitter,NULL);
}
bool ReportingTable::create(const char* paramName)
{
+ // add this report name to the batch map
+ mLock.lock();
+ if (mBatch.find(paramName) == mBatch.end()) {
+ mBatch[paramName] = 0;
+ }
+ mLock.unlock();
+
+ // and to the database
char cmd[200];
sprintf(cmd,"INSERT OR IGNORE INTO REPORTING (NAME,CLEAREDTIME) VALUES (\"%s\",%ld)", paramName, time(NULL));
if (!sqlite3_command(mDB,cmd)) {
@@ -71,12 +85,10 @@ bool ReportingTable::create(const char* paramName)
bool ReportingTable::incr(const char* paramName)
{
- char cmd[200];
- sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+1, UPDATETIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
- if (!sqlite3_command(mDB,cmd)) {
- gLogEarly(LOG_CRIT|mFacility, "cannot increment reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
- return false;
- }
+ mLock.lock();
+ mBatch[paramName]++;
+ mLock.unlock();
+
return true;
}
@@ -106,6 +118,19 @@ bool ReportingTable::clear(const char* paramName)
}
+
+bool ReportingTable::clear()
+{
+ char cmd[200];
+ sprintf(cmd,"UPDATE REPORTING SET VALUE=0, UPDATETIME=0, CLEAREDTIME=%ld", time(NULL));
+ if (!sqlite3_command(mDB,cmd)) {
+ gLogEarly(LOG_CRIT|mFacility, "cannot clear reporting table, error message: %s", sqlite3_errmsg(mDB));
+ return false;
+ }
+ return true;
+}
+
+
bool ReportingTable::create(const char* baseName, unsigned minIndex, unsigned maxIndex)
{
size_t sz = strlen(baseName);
@@ -140,6 +165,53 @@ bool ReportingTable::clear(const char* baseName, unsigned index)
return clear(name);
}
+bool ReportingTable::commit()
+{
+ ReportBatch oustanding;
+ ReportBatch::iterator mp;
+ unsigned oustandingCount = 0;
+ // copy out to free up access to mBatch as quickly as possible
+ mLock.lock();
+ mp = mBatch.begin();
+ while (mp != mBatch.end()) {
+ if (mp->second > 0) {
+ oustanding[mp->first] = mp->second;
+ mBatch[mp->first] = 0;
+ oustandingCount++;
+ }
+ mp++;
+ }
+ mLock.unlock();
+ // now actually write them into the db if needed
+ if (oustandingCount > 0) {
+ Timeval timer;
+ char cmd[200];
+ // TODO : could wrap this in a BEGIN; COMMIT; pair to transactionize these X UPDATEs
+ mp = oustanding.begin();
+ while (mp != oustanding.end()) {
+ sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+%u, UPDATETIME=%ld WHERE NAME=\"%s\"", mp->second, time(NULL), mp->first.c_str());
+ if (!sqlite3_command(mDB,cmd)) {
+ LOG(CRIT) << "could not increment reporting parameter " << mp->first << ", error message: " << sqlite3_errmsg(mDB);
+ }
+ mp++;
+ }
+
+ LOG(INFO) << "wrote " << oustandingCount << " entries in " << timer.elapsed() << "ms";
+ }
+
+ return true;
+}
+
+extern ReportingTable gReports;
+void* reportingBatchCommitter(void*)
+{
+ while (true) {
+ sleep(10);
+ gReports.commit();
+ }
+
+ return NULL;
+}
diff --git a/Reporting.h b/Reporting.h
index 1878618..399727c 100644
--- a/Reporting.h
+++ b/Reporting.h
@@ -1,6 +1,6 @@
/**@file Module for performance-reporting mechanisms. */
/*
-* Copyright 2012 Range Networks, Inc.
+* Copyright 2012, 2013 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -28,7 +28,12 @@
#include
#include
+#include