4 * GDBM database driver for Citadel/UX
9 * Note that each call to a GDBM function is wrapped in an S_DATABASE critical
10 * section. This is done because GDBM is not threadsafe. This is the ONLY
11 * place in the entire Citadel server where any code enters two different
12 * classes of critical sections at the same time; this is why the GDBM calls
13 * are *tightly* wrapped in S_DATABASE. Opening multiple concurrent critical
14 * sections elsewhere in the code can, and probably will, cause deadlock
15 * conditions to occur. (Deadlock is bad. Eliminate.)
27 #if TIME_WITH_SYS_TIME
28 # include <sys/time.h>
32 # include <sys/time.h>
45 #include "sysdep_decls.h"
49 * This array holds one gdbm handle for each Citadel database.
51 GDBM_FILE gdbms[MAXCDB];
54 * We also keep these around, for sequential searches (one per session slot)
61 * Reclaim unused space in the databases. We need to do each one of
62 * these discretely, rather than in a loop.
64 void defrag_databases(void)
67 /* defrag the message base */
68 lprintf(7, "Defragmenting message base\n");
69 begin_critical_section(S_DATABASE);
70 gdbm_reorganize(gdbms[CDB_MSGMAIN]);
71 end_critical_section(S_DATABASE);
73 /* defrag the user file, mailboxes, and user/room relationships */
74 lprintf(7, "Defragmenting user file\n");
75 begin_critical_section(S_USERSUPP);
76 begin_critical_section(S_DATABASE);
77 gdbm_reorganize(gdbms[CDB_USERSUPP]);
78 gdbm_reorganize(gdbms[CDB_VISIT]);
79 end_critical_section(S_DATABASE);
80 end_critical_section(S_USERSUPP);
82 /* defrag the room files and message lists */
83 lprintf(7, "Defragmenting room files and message lists\n");
84 begin_critical_section(S_QUICKROOM);
85 begin_critical_section(S_DATABASE);
86 gdbm_reorganize(gdbms[CDB_QUICKROOM]);
87 gdbm_reorganize(gdbms[CDB_MSGLISTS]);
88 end_critical_section(S_DATABASE);
89 end_critical_section(S_QUICKROOM);
91 /* defrag the floor table */
92 lprintf(7, "Defragmenting floor table\n");
93 begin_critical_section(S_FLOORTAB);
94 begin_critical_section(S_DATABASE);
95 gdbm_reorganize(gdbms[CDB_FLOORTAB]);
96 end_critical_section(S_DATABASE);
97 end_critical_section(S_FLOORTAB);
102 * Open the various gdbm databases we'll be using. Any database which
103 * does not exist should be created.
105 void open_databases(void)
107 lprintf(7, "%s\n", gdbm_version);
110 * Silently try to create the database subdirectory. If it's
111 * already there, no problem.
113 system("exec mkdir data 2>/dev/null");
115 /* a critical section is unnecessary, as this function is called before
116 any other threads are created. and it causes problems on BSDI.
118 begin_critical_section(S_DATABASE);
122 gdbms[CDB_MSGMAIN] = gdbm_open("data/msgmain.gdbm", 8192,
123 GDBM_WRCREAT, 0600, NULL);
124 if (gdbms[CDB_MSGMAIN] == NULL) {
125 lprintf(2, "Cannot open msgmain: %s\n",
126 gdbm_strerror(gdbm_errno));
129 gdbms[CDB_USERSUPP] = gdbm_open("data/usersupp.gdbm", 0,
130 GDBM_WRCREAT, 0600, NULL);
131 if (gdbms[CDB_USERSUPP] == NULL) {
132 lprintf(2, "Cannot open usersupp: %s\n",
133 gdbm_strerror(gdbm_errno));
136 gdbms[CDB_VISIT] = gdbm_open("data/visit.gdbm", 0,
137 GDBM_WRCREAT, 0600, NULL);
138 if (gdbms[CDB_VISIT] == NULL) {
139 lprintf(2, "Cannot open visit file: %s\n",
140 gdbm_strerror(gdbm_errno));
143 gdbms[CDB_QUICKROOM] = gdbm_open("data/quickroom.gdbm", 0,
144 GDBM_WRCREAT, 0600, NULL);
145 if (gdbms[CDB_QUICKROOM] == NULL) {
146 lprintf(2, "Cannot open quickroom: %s\n",
147 gdbm_strerror(gdbm_errno));
150 gdbms[CDB_FLOORTAB] = gdbm_open("data/floortab.gdbm", 0,
151 GDBM_WRCREAT, 0600, NULL);
152 if (gdbms[CDB_FLOORTAB] == NULL) {
153 lprintf(2, "Cannot open floortab: %s\n",
154 gdbm_strerror(gdbm_errno));
157 gdbms[CDB_MSGLISTS] = gdbm_open("data/msglists.gdbm", 0,
158 GDBM_WRCREAT, 0600, NULL);
159 if (gdbms[CDB_MSGLISTS] == NULL) {
160 lprintf(2, "Cannot open msglists: %s\n",
161 gdbm_strerror(gdbm_errno));
165 end_critical_section(S_DATABASE);
172 * Close all of the gdbm database files we've opened. This can be done
173 * in a loop, since it's just a bunch of closes.
175 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 gdbm_close(gdbms[a]);
184 end_critical_section(S_DATABASE);
186 for (a = 0; a < max_keys; ++a) {
187 if (dtkey[a].dptr != NULL) {
188 phree(dtkey[a].dptr);
196 * Store a piece of data. Returns 0 if the operation was successful. If a
197 * datum already exists it should be overwritten.
199 int cdb_store(int cdb,
200 void *key, int keylen,
201 void *data, int datalen)
209 ddata.dsize = datalen;
212 begin_critical_section(S_DATABASE);
213 retval = gdbm_store(gdbms[cdb], dkey, ddata, GDBM_REPLACE);
214 end_critical_section(S_DATABASE);
216 lprintf(2, "gdbm error: %s\n", gdbm_strerror(gdbm_errno));
224 * Delete a piece of data. Returns 0 if the operation was successful.
226 int cdb_delete(int cdb, void *key, int keylen)
235 begin_critical_section(S_DATABASE);
236 retval = gdbm_delete(gdbms[cdb], dkey);
237 end_critical_section(S_DATABASE);
246 * Fetch a piece of data. If not found, returns NULL. Otherwise, it returns
247 * a struct cdbdata which it is the caller's responsibility to free later on
248 * using the cdb_free() routine.
250 struct cdbdata *cdb_fetch(int cdb, void *key, int keylen)
253 struct cdbdata *tempcdb;
259 begin_critical_section(S_DATABASE);
260 dret = gdbm_fetch(gdbms[cdb], dkey);
261 end_critical_section(S_DATABASE);
262 if (dret.dptr == NULL) {
265 tempcdb = (struct cdbdata *) mallok(sizeof(struct cdbdata));
266 if (tempcdb == NULL) {
267 lprintf(2, "Cannot allocate memory!\n");
269 tempcdb->len = dret.dsize;
270 tempcdb->ptr = dret.dptr;
276 * Free a cdbdata item (ok, this is really no big deal, but we might need to do
277 * more complex stuff with other database managers in the future).
279 void cdb_free(struct cdbdata *cdb)
285 void cdb_close_cursor(cdb)
287 while (max_keys <= CC->cs_pid) {
291 mallok((sizeof(datum) * max_keys));
294 reallok(dtkey, (sizeof(datum) * max_keys));
296 dtkey[max_keys - 1].dsize = 0;
297 dtkey[max_keys - 1].dptr = NULL;
300 if (dtkey[CC->cs_pid].dptr != NULL) {
301 phree(dtkey[CC->cs_pid].dptr);
303 dtkey[CC->cs_pid].dptr = NULL;
304 dtkey[CC->cs_pid].dsize = 0;
309 * Prepare for a sequential search of an entire database. (In the gdbm model,
310 * we do this by keeping an array dtkey[] of "the next" key for each session
311 * that is open. There is guaranteed to be no more than one traversal in
312 * progress per session at any given time.)
314 void cdb_rewind(int cdb)
317 while (max_keys <= CC->cs_pid) {
321 mallok((sizeof(datum) * max_keys));
324 reallok(dtkey, (sizeof(datum) * max_keys));
326 dtkey[max_keys - 1].dsize = 0;
327 dtkey[max_keys - 1].dptr = NULL;
330 if (dtkey[CC->cs_pid].dptr != NULL) {
331 phree(dtkey[CC->cs_pid].dptr);
333 begin_critical_section(S_DATABASE);
334 dtkey[CC->cs_pid] = gdbm_firstkey(gdbms[cdb]);
335 end_critical_section(S_DATABASE);
340 * Fetch the next item in a sequential search. Returns a pointer to a
341 * cdbdata structure, or NULL if we've hit the end.
343 struct cdbdata *cdb_next_item(int cdb)
346 struct cdbdata *cdbret;
350 if (dtkey[CC->cs_pid].dptr == NULL) { /* end of file */
353 begin_critical_section(S_DATABASE);
354 dret = gdbm_fetch(gdbms[cdb], dtkey[CC->cs_pid]);
355 end_critical_section(S_DATABASE);
356 if (dret.dptr == NULL) { /* bad read */
357 phree(dtkey[CC->cs_pid].dptr);
360 cdbret = (struct cdbdata *) mallok(sizeof(struct cdbdata));
361 cdbret->len = dret.dsize;
362 cdbret->ptr = dret.dptr;
364 ptr = dtkey[CC->cs_pid].dptr;
365 begin_critical_section(S_DATABASE);
366 dtkey[CC->cs_pid] = gdbm_nextkey(gdbms[cdb], dtkey[CC->cs_pid]);
367 end_critical_section(S_DATABASE);
369 if (ptr != NULL) { /* Free the previous key. */
378 * empty functions because GDBM doesn't have transaction support
381 void cdb_begin_transaction(void) {
384 void cdb_end_transaction(void) {
387 void cdb_allocate_tsd(void) {
390 void cdb_free_tsd(void) {
393 void cdb_check_handles(void) {