4 * Sleepycat (Berkeley) DB driver for Citadel/UX
19 #include "citserver.h"
21 #include "sysdep_decls.h"
25 * This array holds one DB handle for each Citadel database.
33 #define MYCURSOR cursorz[CC->cs_pid]
36 * Reclaim unused space in the databases. We need to do each one of
37 * these discretely, rather than in a loop.
39 * This is a stub function in the Sleepycat DB backend, because there is no
40 * such API call available.
42 void defrag_databases(void)
49 * Open the various databases we'll be using. Any database which
50 * does not exist should be created. Note that we don't need an S_DATABASE
51 * critical section here, because there aren't any active threads manipulating
52 * the database yet -- and besides, it causes problems on BSDI.
54 void open_databases(void)
61 * Silently try to create the database subdirectory. If it's
62 * already there, no problem.
64 system("exec mkdir data 2>/dev/null");
66 lprintf(9, "Setting up DB environment\n");
67 ret = db_env_create(&dbenv, 0);
69 lprintf(1, "db_env_create: %s\n", db_strerror(ret));
72 dbenv->set_errpfx(dbenv, "citserver");
75 * We want to specify the shared memory buffer pool cachesize,
76 * but everything else is the default.
78 ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0);
80 lprintf(1, "set_cachesize: %s\n", db_strerror(ret));
81 dbenv->close(dbenv, 0);
86 * We specify DB_PRIVATE but not DB_INIT_LOCK or DB_THREAD, even
87 * though this is a multithreaded application. Since Citadel does all
88 * database access in S_DATABASE critical sections, access to the db
89 * is serialized already, so don't bother the database manager with
90 * it. Besides, it locks up when we do it that way.
92 /* (void)dbenv->set_data_dir(dbenv, "/database/files"); */
93 ret = dbenv->open(dbenv, "./data",
94 ( DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE ),
97 lprintf(1, "dbenv->open: %s\n", db_strerror(ret));
98 dbenv->close(dbenv, 0);
102 lprintf(7, "Starting up DB\n");
104 for (i = 0; i < MAXCDB; ++i) {
106 /* Create a database handle */
107 ret = db_create(&dbp[i], dbenv, 0);
109 lprintf(1, "db_create: %s\n", db_strerror(ret));
114 /* Arbitrary names for our tables -- we reference them by
115 * number, so we don't have string names for them.
117 sprintf(dbfilename, "cdb.%02x", i);
119 ret = dbp[i]->open(dbp[i],
126 lprintf(1, "db_open[%d]: %s\n", i, db_strerror(ret));
136 * Close all of the gdbm database files we've opened. This can be done
137 * in a loop, since it's just a bunch of closes.
139 void close_databases(void)
144 begin_critical_section(S_DATABASE);
145 for (a = 0; a < MAXCDB; ++a) {
146 lprintf(7, "Closing database %d\n", a);
147 ret = dbp[a]->close(dbp[a], 0);
149 lprintf(1, "db_close: %s\n", db_strerror(ret));
156 /* Close the handle. */
157 ret = dbenv->close(dbenv, 0);
159 lprintf(1, "DBENV->close: %s\n", db_strerror(ret));
163 end_critical_section(S_DATABASE);
169 * Store a piece of data. Returns 0 if the operation was successful. If a
170 * key already exists it should be overwritten.
172 int cdb_store(int cdb,
173 void *ckey, int ckeylen,
174 void *cdata, int cdatalen)
180 memset(&dkey, 0, sizeof(DBT));
181 memset(&ddata, 0, sizeof(DBT));
184 ddata.size = cdatalen;
187 begin_critical_section(S_DATABASE);
188 ret = dbp[cdb]->put(dbp[cdb], /* db */
189 NULL, /* transaction ID (hmm...) */
193 end_critical_section(S_DATABASE);
195 lprintf(1, "cdb_store: %s\n", db_strerror(ret));
203 * Delete a piece of data. Returns 0 if the operation was successful.
205 int cdb_delete(int cdb, void *key, int keylen)
214 begin_critical_section(S_DATABASE);
215 ret = dbp[cdb]->del(dbp[cdb], NULL, &dkey, 0);
216 end_critical_section(S_DATABASE);
225 * Fetch a piece of data. If not found, returns NULL. Otherwise, it returns
226 * a struct cdbdata which it is the caller's responsibility to free later on
227 * using the cdb_free() routine.
229 struct cdbdata *cdb_fetch(int cdb, void *key, int keylen)
232 struct cdbdata *tempcdb;
236 memset(&dkey, 0, sizeof(DBT));
237 memset(&dret, 0, sizeof(DBT));
240 dret.flags = DB_DBT_MALLOC;
242 begin_critical_section(S_DATABASE);
243 ret = dbp[cdb]->get(dbp[cdb], NULL, &dkey, &dret, 0);
244 end_critical_section(S_DATABASE);
245 if ((ret != 0) && (ret != DB_NOTFOUND)) {
246 lprintf(1, "cdb_fetch: %s\n", db_strerror(ret));
248 if (ret != 0) return NULL;
249 tempcdb = (struct cdbdata *) mallok(sizeof(struct cdbdata));
250 if (tempcdb == NULL) {
251 lprintf(2, "Cannot allocate memory!\n");
253 tempcdb->len = dret.size;
254 tempcdb->ptr = dret.data;
260 * Free a cdbdata item (ok, this is really no big deal, but we might need to do
261 * more complex stuff with other database managers in the future).
263 void cdb_free(struct cdbdata *cdb)
271 * Prepare for a sequential search of an entire database.
272 * (There is guaranteed to be no more than one traversal in
273 * progress per session at any given time.)
275 void cdb_rewind(int cdb)
280 * Make sure we have a cursor allocated for this session
283 if (num_cursorz <= CC->cs_pid) {
284 num_cursorz = CC->cs_pid + 1;
285 if (cursorz == NULL) {
287 mallok((sizeof(DBC *) * num_cursorz));
290 reallok(cursorz, (sizeof(DBC *) * num_cursorz));
296 * Now initialize the cursor
298 begin_critical_section(S_DATABASE);
299 ret = dbp[cdb]->cursor(dbp[cdb], NULL, &MYCURSOR, 0);
301 lprintf(1, "db_cursor: %s\n", db_strerror(ret));
303 end_critical_section(S_DATABASE);
308 * Fetch the next item in a sequential search. Returns a pointer to a
309 * cdbdata structure, or NULL if we've hit the end.
311 struct cdbdata *cdb_next_item(int cdb)
314 struct cdbdata *cdbret;
317 /* Initialize the key/data pair so the flags aren't set. */
318 memset(&key, 0, sizeof(key));
319 memset(&data, 0, sizeof(data));
320 data.flags = DB_DBT_MALLOC;
322 begin_critical_section(S_DATABASE);
323 ret = MYCURSOR->c_get(MYCURSOR,
324 &key, &data, DB_NEXT);
325 end_critical_section(S_DATABASE);
327 if (ret) return NULL; /* presumably, end of file */
329 cdbret = (struct cdbdata *) mallok(sizeof(struct cdbdata));
330 cdbret->len = data.size;
331 cdbret->ptr = data.data;