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