]> code.citadel.org Git - citadel.git/blob - citadel/database_sleepycat.c
* database
[citadel.git] / citadel / database_sleepycat.c
1 /*
2  * $Id$
3  *
4  * Sleepycat (Berkeley) DB driver for Citadel/UX
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <time.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <db.h>
17 #include "citadel.h"
18 #include "server.h"
19 #include "citserver.h"
20 #include "database.h"
21 #include "sysdep_decls.h"
22
23
24 /*
25  * This array holds one DB handle for each Citadel database.
26  */
27 DB *dbp[MAXCDB];
28
29 DB_ENV *dbenv;
30
31 DBC *MYCURSOR;  /* FIXME !! */
32
33 /*
34  * Reclaim unused space in the databases.  We need to do each one of
35  * these discretely, rather than in a loop.
36  */
37 void defrag_databases(void)
38 {
39         /* FIXME ... do we even need this?  If not, we'll just keep it as
40          * a stub function to keep the API consistent.
41          */
42 }
43
44
45 /*
46  * Open the various databases we'll be using.  Any database which
47  * does not exist should be created.  Note that we don't need an S_DATABASE
48  * critical section here, because there aren't any active threads manipulating
49  * the database yet -- and besides, it causes problems on BSDI.
50  */
51 void open_databases(void)
52 {
53         int ret;
54         int i;
55         char dbfilename[256];
56
57         /*
58          * Silently try to create the database subdirectory.  If it's
59          * already there, no problem.
60          */
61         system("exec mkdir data 2>/dev/null");
62
63         lprintf(9, "Setting up DB environment\n");
64         ret = db_env_create(&dbenv, 0);
65         if (ret) {
66                 lprintf(1, "db_env_create: %s\n", db_strerror(ret));
67                 exit(ret);
68         }
69         dbenv->set_errfile(dbenv, stderr);  /* FIXME */
70         dbenv->set_errpfx(dbenv, "citserver");
71
72         /*
73          * We want to specify the shared memory buffer pool cachesize,
74          * but everything else is the default.
75          */
76         ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0);
77         if (ret) {
78                 lprintf(1, "set_cachesize: %s\n", db_strerror(ret));
79                 dbenv->close(dbenv, 0);
80                 exit(ret);
81         }
82
83         /*
84          * We have multiple processes reading/writing these files, so
85          * we need concurrency control and a shared buffer pool, but
86          * not logging or transactions.
87          */
88         /* (void)dbenv->set_data_dir(dbenv, "/database/files"); */
89         ret = dbenv->open(dbenv, "./data",
90          DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0);
91         if (ret) {
92                 lprintf(1, "dbenv->open: %s\n", db_strerror(ret));
93                 dbenv->close(dbenv, 0);
94                 exit(ret);
95         }
96
97         lprintf(7, "Starting up DB\n");
98
99         for (i = 0; i < MAXCDB; ++i) {
100
101                 /* Create a database handle */
102                 ret = db_create(&dbp[i], dbenv, 0);
103                 if (ret) {
104                         lprintf(1, "db_create: %s\n", db_strerror(ret));
105                         exit(ret);
106                 }
107
108
109                 /* Arbitrary names for our tables -- we reference them by
110                  * number, so we don't have string names for them.
111                  */
112                 sprintf(dbfilename, "cdb.%02x", i);
113
114                 ret = dbp[i]->open(dbp[i],
115                                 dbfilename,
116                                 NULL,
117                                 DB_BTREE,
118                                 DB_CREATE,
119                                 0600);
120                 if (ret) {
121                         lprintf(1, "db_open[%d]: %s\n", i, db_strerror(ret));
122                         exit(ret);
123                 }
124
125         }
126
127 }
128
129
130 /*
131  * Close all of the gdbm database files we've opened.  This can be done
132  * in a loop, since it's just a bunch of closes.
133  */
134 void close_databases(void)
135 {
136         int a;
137         int ret;
138
139         begin_critical_section(S_DATABASE);
140         for (a = 0; a < MAXCDB; ++a) {
141                 lprintf(7, "Closing database %d\n", a);
142                 ret = dbp[a]->close(dbp[a], 0);
143                 if (ret) {
144                         lprintf(1, "db_close: %s\n", db_strerror(ret));
145                 }
146                 
147         }
148
149
150
151         /* Close the handle. */
152         ret = dbenv->close(dbenv, 0);
153         if (ret) {
154                 lprintf(1, "DBENV->close: %s\n", db_strerror(ret));
155         }
156
157
158         end_critical_section(S_DATABASE);
159
160 }
161
162
163 /*
164  * Store a piece of data.  Returns 0 if the operation was successful.  If a
165  * key already exists it should be overwritten.
166  */
167 int cdb_store(int cdb,
168               void *ckey, int ckeylen,
169               void *cdata, int cdatalen)
170 {
171
172         DBT dkey, ddata;
173         int ret;
174
175         memset(&dkey, 0, sizeof(DBT));
176         memset(&ddata, 0, sizeof(DBT));
177         dkey.size = ckeylen;
178         dkey.data = ckey;
179         ddata.size = cdatalen;
180         ddata.data = cdata;
181
182         begin_critical_section(S_DATABASE);
183         ret = dbp[cdb]->put(dbp[cdb],           /* db */
184                                 NULL,           /* transaction ID (hmm...) */
185                                 &dkey,          /* key */
186                                 &ddata,         /* data */
187                                 0);             /* flags */
188         end_critical_section(S_DATABASE);
189         lprintf(9, "put (  to file %d) returned %3d (%d bytes)\n",
190                 cdb, ret, ddata.size);
191         if (ret) {
192                 lprintf(1, "cdb_store: %s\n", db_strerror(ret));
193                 return (-1);
194         }
195         return (0);
196 }
197
198
199 /*
200  * Delete a piece of data.  Returns 0 if the operation was successful.
201  */
202 int cdb_delete(int cdb, void *key, int keylen)
203 {
204
205         DBT dkey;
206         int ret;
207
208         dkey.size = keylen;
209         dkey.data = key;
210
211         begin_critical_section(S_DATABASE);
212         ret = dbp[cdb]->del(dbp[cdb], NULL, &dkey, 0);
213         end_critical_section(S_DATABASE);
214         return (ret);
215
216 }
217
218
219
220
221 /*
222  * Fetch a piece of data.  If not found, returns NULL.  Otherwise, it returns
223  * a struct cdbdata which it is the caller's responsibility to free later on
224  * using the cdb_free() routine.
225  */
226 struct cdbdata *cdb_fetch(int cdb, void *key, int keylen)
227 {
228
229         struct cdbdata *tempcdb;
230         DBT dkey, dret;
231         int ret;
232
233         memset(&dkey, 0, sizeof(DBT));
234         memset(&dret, 0, sizeof(DBT));
235         dkey.size = keylen;
236         dkey.data = key;
237         dret.flags = DB_DBT_MALLOC;
238
239         begin_critical_section(S_DATABASE);
240         ret = dbp[cdb]->get(dbp[cdb], NULL, &dkey, &dret, 0);
241         end_critical_section(S_DATABASE);
242         lprintf(9, "get (from file %d) returned %3d (%d bytes)\n",
243                 cdb, ret, dret.size);
244         if ((ret != 0) && (ret != DB_NOTFOUND)) {
245                 lprintf(1, "cdb_fetch: %s\n", db_strerror(ret));
246         }
247         if (ret != 0) return NULL;
248         tempcdb = (struct cdbdata *) mallok(sizeof(struct cdbdata));
249         if (tempcdb == NULL) {
250                 lprintf(2, "Cannot allocate memory!\n");
251         }
252         tempcdb->len = dret.size;
253         tempcdb->ptr = dret.data;
254         return (tempcdb);
255 }
256
257
258 /*
259  * Free a cdbdata item (ok, this is really no big deal, but we might need to do
260  * more complex stuff with other database managers in the future).
261  */
262 void cdb_free(struct cdbdata *cdb)
263 {
264         phree(cdb->ptr);
265         phree(cdb);
266 }
267
268
269 /* 
270  * Prepare for a sequential search of an entire database.  (In the DB model,
271  * use per-session key. There is guaranteed to be no more than one traversal in
272  * progress per session at any given time.)
273  */
274 void cdb_rewind(int cdb)
275 {
276         int ret = 0;
277
278         begin_critical_section(S_DATABASE);
279         ret = dbp[cdb]->cursor(dbp[cdb], NULL, &MYCURSOR, 0);
280         if (ret) {
281                 lprintf(1, "db_cursor: %s\n", db_strerror(ret));
282         }
283         end_critical_section(S_DATABASE);
284 }
285
286
287 /*
288  * Fetch the next item in a sequential search.  Returns a pointer to a 
289  * cdbdata structure, or NULL if we've hit the end.
290  */
291 struct cdbdata *cdb_next_item(int cdb)
292 {
293         DBT key, data;
294         struct cdbdata *cdbret;
295         int ret = 0;
296
297         /* Initialize the key/data pair so the flags aren't set. */
298         memset(&key, 0, sizeof(key));
299         memset(&data, 0, sizeof(data));
300         data.flags = DB_DBT_MALLOC;
301
302         begin_critical_section(S_DATABASE);
303         ret = MYCURSOR->c_get(MYCURSOR,
304                 &key, &data, DB_NEXT);
305         end_critical_section(S_DATABASE);
306         
307         if (ret) return NULL;           /* presumably, end of file */
308
309         cdbret = (struct cdbdata *) mallok(sizeof(struct cdbdata));
310         cdbret->len = data.size;
311         cdbret->ptr = data.data;
312
313         return (cdbret);
314 }