blob: 3ad4f010c1bc6718cb3c1074d49d1d280a9d24d8 [file] [log] [blame]
dburgess82c46ff2011-10-07 02:40:51 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3* Copyright 2010 Kestrel Signal Processing, Inc.
4*
5*
6* This software is distributed under the terms of the GNU Affero Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Affero General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Affero General Public License for more details.
21
22 You should have received a copy of the GNU Affero General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25*/
26
27
28#include "Configuration.h"
29#include <fstream>
30#include <iostream>
31#include <string.h>
32#include <syslog.h>
33
34using namespace std;
35
36
37static const char* createConfigTable = {
38 "CREATE TABLE IF NOT EXISTS CONFIG ("
39 "KEYSTRING TEXT UNIQUE NOT NULL, "
40 "VALUESTRING TEXT, "
41 "STATIC INTEGER DEFAULT 0, "
42 "OPTIONAL INTEGER DEFAULT 0, "
43 "COMMENTS TEXT DEFAULT ''"
44 ")"
45};
46
47
48ConfigurationTable::ConfigurationTable(const char* filename)
49{
50 // Connect to the database.
51 int rc = sqlite3_open(filename,&mDB);
52 if (rc) {
53 cerr << "Cannot open configuration database: " << sqlite3_errmsg(mDB);
54 sqlite3_close(mDB);
55 mDB = NULL;
56 return;
57 }
58 // Create the table, if needed.
59 if (!sqlite3_command(mDB,createConfigTable)) {
60 cerr << "Cannot create configuration table:" << sqlite3_errmsg(mDB);
61 }
62}
63
64
65
66bool ConfigurationTable::defines(const string& key)
67{
68 assert(mDB);
69 ScopedLock lock(mLock);
70
71 // Check the cache.
72 checkCacheAge();
73 ConfigurationMap::const_iterator where = mCache.find(key);
74 if (where!=mCache.end()) return where->second.defined();
75
76 // Check the database.
77 char *value = NULL;
78 sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",value);
79
80 // Cache the result.
81 if (value) {
82 mCache[key] = ConfigurationRecord(value);
83 free(value);
84 return true;
85 }
86
87 mCache[key] = ConfigurationRecord(false);
88 return false;
89}
90
91
92const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
93{
94 assert(mDB);
95 checkCacheAge();
96 // We assume the caller holds mLock.
97 // So it is OK to return a reference into the cache.
98
99 // Check the cache.
100 // This is cheap.
101 ConfigurationMap::const_iterator where = mCache.find(key);
102 if (where!=mCache.end()) {
103 if (where->second.defined()) return where->second;
104 // Unlock the mutex before throwing the exception.
105 mLock.unlock();
106 syslog(LOG_ALERT, "configuration key %s not found", key.c_str());
107 throw ConfigurationTableKeyNotFound(key);
108 }
109
110 // Check the database.
111 // This is more expensive.
112 char *value = NULL;
113 sqlite3_single_lookup(mDB,"CONFIG",
114 "KEYSTRING",key.c_str(),"VALUESTRING",value);
115
116 // Nothing defined?
117 if (!value) {
118 // Cache the failure.
119 mCache[key] = ConfigurationRecord(false);
120 // Unlock the mutex before throwing the exception.
121 mLock.unlock();
122 throw ConfigurationTableKeyNotFound(key);
123 }
124
125 // Cache the result.
126 mCache[key] = ConfigurationRecord(value);
127 free(value);
128
129 // Leave mLock locked. The caller holds it still.
130 return mCache[key];
131}
132
133
134
135bool ConfigurationTable::isStatic(const string& key) const
136{
137 assert(mDB);
138 unsigned stat;
139 bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"STATIC",stat);
140 if (success) return (bool)stat;
141 return false;
142}
143
144bool ConfigurationTable::isRequired(const string& key) const
145{
146 assert(mDB);
147 unsigned optional;
148 bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"OPTIONAL",optional);
149 if (success) return !((bool)optional);
150 return false;
151}
152
153
154
155
156string ConfigurationTable::getStr(const string& key)
157{
158 // We need the lock because rec is a reference into the cache.
159 ScopedLock lock(mLock);
160 return lookup(key).value();
161}
162
163string ConfigurationTable::getStr(const string& key, const char* defaultValue)
164{
165 try {
166 return getStr(key);
167 } catch (ConfigurationTableKeyNotFound) {
168 set(key,defaultValue);
169 return string(defaultValue);
170 }
171}
172
173
174long ConfigurationTable::getNum(const string& key)
175{
176 // We need the lock because rec is a reference into the cache.
177 ScopedLock lock(mLock);
178 return lookup(key).number();
179}
180
181
182long ConfigurationTable::getNum(const string& key, long defaultValue)
183{
184 try {
185 return getNum(key);
186 } catch (ConfigurationTableKeyNotFound) {
187 set(key,defaultValue);
188 return defaultValue;
189 }
190}
191
192
193
194std::vector<unsigned> ConfigurationTable::getVector(const string& key)
195{
196 // Look up the string.
197 mLock.lock();
198 const ConfigurationRecord& rec = lookup(key);
199 char* line = strdup(rec.value().c_str());
200 mLock.unlock();
201 // Parse the string.
202 std::vector<unsigned> retVal;
203 char *lp=line;
204 while (lp) {
205 // Watch for multiple or trailing spaces.
206 while (*lp==' ') lp++;
207 if (*lp=='\0') break;
208 retVal.push_back(strtol(lp,NULL,0));
209 strsep(&lp," ");
210 }
211 free(line);
212 return retVal;
213}
214
215
216bool ConfigurationTable::unset(const string& key)
217{
218 assert(mDB);
219 if (!defines(key)) return true;
220 if (isRequired(key)) return false;
221
222 ScopedLock lock(mLock);
223 // Clear the cache entry and the database.
224 ConfigurationMap::iterator where = mCache.find(key);
225 if (where!=mCache.end()) mCache.erase(where);
226 // Don't delete it; just set VALUESTRING to NULL.
227 string cmd = "UPDATE CONFIG SET VALUESTRING=NULL WHERE KEYSTRING=='"+key+"'";
228 return sqlite3_command(mDB,cmd.c_str());
229}
230
231
232void ConfigurationTable::find(const string& pat, ostream& os) const
233{
234 // Prepare the statement.
235 string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG WHERE KEYSTRING LIKE \"%" + pat + "%\"";
236 sqlite3_stmt *stmt;
237 if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return;
238 // Read the result.
239 int src = sqlite3_run_query(mDB,stmt);
240 while (src==SQLITE_ROW) {
241 const char* value = (const char*)sqlite3_column_text(stmt,1);
242 os << sqlite3_column_text(stmt,0) << " ";
243 if (value) os << value << endl;
244 else os << "(null)" << endl;
245 src = sqlite3_run_query(mDB,stmt);
246 }
247 sqlite3_finalize(stmt);
248}
249
250
251bool ConfigurationTable::set(const string& key, const string& value)
252{
253 assert(mDB);
254 ScopedLock lock(mLock);
255 // Is it there already?
256 char * oldValue = NULL;
257 bool exists = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",oldValue);
258 // Update or insert as appropriate.
259 string cmd;
260 if (exists) cmd = "UPDATE CONFIG SET VALUESTRING=\""+value+"\" WHERE KEYSTRING==\""+key+"\"";
261 else cmd = "INSERT INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)";
262 bool success = sqlite3_command(mDB,cmd.c_str());
263 // Cache the result.
264 if (success) mCache[key] = ConfigurationRecord(value);
265 return success;
266}
267
268bool ConfigurationTable::set(const string& key, long value)
269{
270 char buffer[30];
271 sprintf(buffer,"%ld",value);
272 return set(key,buffer);
273}
274
275
276bool ConfigurationTable::set(const string& key)
277{
278 assert(mDB);
279 ScopedLock lock(mLock);
280 string cmd = "INSERT INTO CONFIG (KEYSTRING) VALUES (\"" + key + "\")";
281 bool success = sqlite3_command(mDB,cmd.c_str());
282 if (success) mCache[key] = ConfigurationRecord(true);
283 return success;
284}
285
286
287void ConfigurationTable::checkCacheAge()
288{
289 // mLock is set by caller
290 static time_t timeOfLastPurge = 0;
291 time_t now = time(NULL);
292 // purge every 3 seconds
293 // purge period cannot be configuration parameter
294 if (now - timeOfLastPurge < 3) return;
295 timeOfLastPurge = now;
296 // this is purge() without the lock
297 ConfigurationMap::iterator mp = mCache.begin();
298 while (mp != mCache.end()) {
299 ConfigurationMap::iterator prev = mp;
300 mp++;
301 mCache.erase(prev);
302 }
303}
304
305
306void ConfigurationTable::purge()
307{
308 ScopedLock lock(mLock);
309 ConfigurationMap::iterator mp = mCache.begin();
310 while (mp != mCache.end()) {
311 ConfigurationMap::iterator prev = mp;
312 mp++;
313 mCache.erase(prev);
314 }
315}
316
317
318void ConfigurationTable::setUpdateHook(void(*func)(void *,int ,char const *,char const *,sqlite3_int64))
319{
320 assert(mDB);
321 sqlite3_update_hook(mDB,func,NULL);
322}
323
324
325
326void HashString::computeHash()
327{
328 // FIXME -- Someone needs to review this hash function.
329 const char* cstr = c_str();
330 mHash = 0;
331 for (unsigned i=0; i<size(); i++) {
332 mHash = mHash ^ (mHash >> 32);
333 mHash = mHash*127 + cstr[i];
334 }
335}
336
337
338
339// vim: ts=4 sw=4