4 * Sleepycat (Berkeley) DB driver for Citadel/UX
8 /*****************************************************************************/
18 #include <sys/types.h>
23 #include "citserver.h"
25 #include "sysdep_decls.h"
26 #include "dynloader.h"
28 DB *dbp[MAXCDB]; /* One DB handle for each Citadel database */
29 DB_ENV *dbenv; /* The DB environment (global) */
31 struct cdbssd { /* Session-specific DB stuff */
32 DBC *cursor; /* Cursor, for traversals... */
35 struct cdbssd *ssd_arr = NULL;
38 #define MYCURSOR ssd_arr[CC->cs_pid].cursor
42 * Ensure that we have enough space for session-specific data. We don't
43 * put anything in here that Citadel cares about; this is just database
44 * related stuff like cursors.
46 void cdb_allocate_ssd(void) {
48 * Make sure we have a cursor allocated for this session
51 lprintf(9, "num_ssd before realloc = %d\n", num_ssd);
52 if (num_ssd <= CC->cs_pid) {
53 num_ssd = CC->cs_pid + 1;
54 if (ssd_arr == NULL) {
55 ssd_arr = (struct cdbssd *)
56 mallok((sizeof(struct cdbssd) * num_ssd));
58 ssd_arr = (struct cdbssd *)
59 reallok(ssd_arr, (sizeof(struct cdbssd) * num_ssd));
62 lprintf(9, "num_ssd after realloc = %d\n", num_ssd);
67 * Reclaim unused space in the databases. We need to do each one of
68 * these discretely, rather than in a loop.
70 * This is a stub function in the Sleepycat DB backend, because there is no
71 * such API call available.
73 void defrag_databases(void)
81 * Open the various databases we'll be using. Any database which
82 * does not exist should be created. Note that we don't need an S_DATABASE
83 * critical section here, because there aren't any active threads manipulating
84 * the database yet -- and besides, it causes problems on BSDI.
86 void open_databases(void)
93 lprintf(9, "open_databases() starting\n");
95 * Silently try to create the database subdirectory. If it's
96 * already there, no problem.
98 system("exec mkdir data 2>/dev/null");
100 lprintf(9, "Setting up DB environment\n");
101 ret = db_env_create(&dbenv, 0);
103 lprintf(1, "db_env_create: %s\n", db_strerror(ret));
106 dbenv->set_errpfx(dbenv, "citserver");
109 * We want to specify the shared memory buffer pool cachesize,
110 * but everything else is the default.
112 ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0);
114 lprintf(1, "set_cachesize: %s\n", db_strerror(ret));
115 dbenv->close(dbenv, 0);
120 * We specify DB_PRIVATE but not DB_INIT_LOCK or DB_THREAD, even
121 * though this is a multithreaded application. Since Citadel does all
122 * database access in S_DATABASE critical sections, access to the db
123 * is serialized already, so don't bother the database manager with
124 * it. Besides, it locks up when we do it that way.
126 flags = DB_CREATE|DB_RECOVER|DB_INIT_MPOOL|DB_PRIVATE|DB_INIT_LOG;
127 /* flags |= DB_INIT_LOCK | DB_THREAD; */
128 ret = dbenv->open(dbenv, "./data", flags, 0);
130 lprintf(1, "dbenv->open: %s\n", db_strerror(ret));
131 dbenv->close(dbenv, 0);
135 lprintf(7, "Starting up DB\n");
137 for (i = 0; i < MAXCDB; ++i) {
139 /* Create a database handle */
140 ret = db_create(&dbp[i], dbenv, 0);
142 lprintf(1, "db_create: %s\n", db_strerror(ret));
147 /* Arbitrary names for our tables -- we reference them by
148 * number, so we don't have string names for them.
150 sprintf(dbfilename, "cdb.%02x", i);
152 ret = dbp[i]->open(dbp[i],
159 lprintf(1, "db_open[%d]: %s\n", i, db_strerror(ret));
165 CtdlRegisterSessionHook(cdb_allocate_ssd, EVT_START);
166 lprintf(9, "open_databases() finished\n");
171 * Close all of the gdbm database files we've opened. This can be done
172 * in a loop, since it's just a bunch of closes.
174 void close_databases(void)
179 begin_critical_section(S_DATABASE);
180 for (a = 0; a < MAXCDB; ++a) {
181 lprintf(7, "Closing database %d\n", a);
182 ret = dbp[a]->close(dbp[a], 0);
184 lprintf(1, "db_close: %s\n", db_strerror(ret));
191 /* Close the handle. */
192 ret = dbenv->close(dbenv, 0);
194 lprintf(1, "DBENV->close: %s\n", db_strerror(ret));
198 end_critical_section(S_DATABASE);
204 * Store a piece of data. Returns 0 if the operation was successful. If a
205 * key already exists it should be overwritten.
207 int cdb_store(int cdb,
208 void *ckey, int ckeylen,
209 void *cdata, int cdatalen)
215 memset(&dkey, 0, sizeof(DBT));
216 memset(&ddata, 0, sizeof(DBT));
219 ddata.size = cdatalen;
222 begin_critical_section(S_DATABASE);
223 ret = dbp[cdb]->put(dbp[cdb], /* db */
224 MYTID, /* transaction ID */
228 end_critical_section(S_DATABASE);
230 lprintf(1, "cdb_store(%d): %s\n", cdb, db_strerror(ret));
238 * Delete a piece of data. Returns 0 if the operation was successful.
240 int cdb_delete(int cdb, void *key, int keylen)
249 begin_critical_section(S_DATABASE);
250 ret = dbp[cdb]->del(dbp[cdb], MYTID, &dkey, 0);
251 end_critical_section(S_DATABASE);
260 * Fetch a piece of data. If not found, returns NULL. Otherwise, it returns
261 * a struct cdbdata which it is the caller's responsibility to free later on
262 * using the cdb_free() routine.
264 struct cdbdata *cdb_fetch(int cdb, void *key, int keylen)
267 struct cdbdata *tempcdb;
271 memset(&dkey, 0, sizeof(DBT));
272 memset(&dret, 0, sizeof(DBT));
275 dret.flags = DB_DBT_MALLOC;
277 begin_critical_section(S_DATABASE);
278 ret = dbp[cdb]->get(dbp[cdb], MYTID, &dkey, &dret, 0);
279 end_critical_section(S_DATABASE);
280 if ((ret != 0) && (ret != DB_NOTFOUND)) {
281 lprintf(1, "cdb_fetch: %s\n", db_strerror(ret));
283 if (ret != 0) return NULL;
284 tempcdb = (struct cdbdata *) mallok(sizeof(struct cdbdata));
285 if (tempcdb == NULL) {
286 lprintf(2, "Cannot allocate memory!\n");
288 tempcdb->len = dret.size;
289 tempcdb->ptr = dret.data;
295 * Free a cdbdata item (ok, this is really no big deal, but we might need to do
296 * more complex stuff with other database managers in the future).
298 void cdb_free(struct cdbdata *cdb)
306 * Prepare for a sequential search of an entire database.
307 * (There is guaranteed to be no more than one traversal in
308 * progress per session at any given time.)
310 void cdb_rewind(int cdb)
315 * Now initialize the cursor
317 begin_critical_section(S_DATABASE);
318 ret = dbp[cdb]->cursor(dbp[cdb], MYTID, &MYCURSOR, 0);
320 lprintf(1, "db_cursor: %s\n", db_strerror(ret));
322 end_critical_section(S_DATABASE);
327 * Fetch the next item in a sequential search. Returns a pointer to a
328 * cdbdata structure, or NULL if we've hit the end.
330 struct cdbdata *cdb_next_item(int cdb)
333 struct cdbdata *cdbret;
336 /* Initialize the key/data pair so the flags aren't set. */
337 memset(&key, 0, sizeof(key));
338 memset(&data, 0, sizeof(data));
339 data.flags = DB_DBT_MALLOC;
341 begin_critical_section(S_DATABASE);
342 ret = MYCURSOR->c_get(MYCURSOR,
343 &key, &data, DB_NEXT);
344 end_critical_section(S_DATABASE);
346 if (ret) return NULL; /* presumably, end of file */
348 cdbret = (struct cdbdata *) mallok(sizeof(struct cdbdata));
349 cdbret->len = data.size;
350 cdbret->ptr = data.data;