2
* jabberd - Jabber Open Source Server
3
* Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4
* Ryan Eatmon, Robert Norris
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
21
/** @file sm/storage_db.c
22
* @brief berkeley db storage module
23
* @author Robert Norris
24
* $Date: 2005/06/02 04:48:25 $
29
* !!! we must catch DB_RUNRECOVERY and call _st_db_panic(). I would argue that
30
* Berkeley should do this for all cases, not just for the process that
31
* caused the fault, but I'm not sure they see it that way. (I have asked,
32
* just waiting for a reply)
34
* Sleepycat SR#7019 resolved this. There is an unreleased patch available
35
* (I have a copy) that will be in 4.2 (due in June).
41
/** internal structure, holds our data */
42
typedef struct drvdata_st {
53
/** internal structure, holds a single db handle */
54
typedef struct dbdata_st {
60
/* union for strict alias rules in gcc3 */
66
static st_ret_t _st_db_add_type(st_driver_t drv, const char *type) {
67
drvdata_t data = (drvdata_t) drv->private;
71
dbd = (dbdata_t) calloc(1, sizeof(struct dbdata_st));
75
if((err = db_create(&(dbd->db), data->env, 0)) != 0) {
76
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't create db handle: %s", db_strerror(err));
81
if((err = dbd->db->set_flags(dbd->db, DB_DUP)) != 0) {
82
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't set database for duplicate storage: %s", db_strerror(err));
83
dbd->db->close(dbd->db, 0);
88
if((err = dbd->db->open(dbd->db, NULL, "sm.db", type, DB_HASH, DB_AUTO_COMMIT | DB_CREATE, 0)) != 0) {
89
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't open storage db: %s", db_strerror(err));
90
dbd->db->close(dbd->db, 0);
95
xhash_put(data->dbs, type, dbd);
100
/** make a new cursor (optionally wrapped in a txn) */
101
static st_ret_t _st_db_cursor_new(st_driver_t drv, dbdata_t dbd, DBC **cursor, DB_TXN **txnid) {
105
if((err = dbd->data->env->txn_begin(dbd->data->env, NULL, txnid, DB_TXN_SYNC)) != 0) {
106
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't begin new transaction: %s", db_strerror(err));
111
err = dbd->db->cursor(dbd->db, NULL, cursor, 0);
113
err = dbd->db->cursor(dbd->db, *txnid, cursor, 0);
116
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't create cursor: %s", db_strerror(err));
118
(*txnid)->abort(*txnid);
125
/** close down a cursor */
126
static st_ret_t _st_db_cursor_free(st_driver_t drv, dbdata_t dbd, DBC *cursor, DB_TXN *txnid) {
129
if((err = cursor->c_close(cursor)) != 0) {
130
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't close cursor: %s", db_strerror(err));
137
if((err = txnid->commit(txnid, DB_TXN_SYNC)) != 0) {
138
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't commit transaction: %s", db_strerror(err));
145
static void _st_db_object_serialise(os_object_t o, char **buf, int *len) {
146
char *key, *xml, *xmlstr;
151
log_debug(ZONE, "serialising object");
156
if(os_object_iter_first(o))
158
os_object_iter_get(o, &key, &val, &ot);
160
log_debug(ZONE, "serialising key %s", key);
162
ser_string_set(key, &cur, buf, len);
163
ser_int_set(ot, &cur, buf, len);
166
case os_type_BOOLEAN:
167
ser_int_set(((int) val) != 0, &cur, buf, len);
170
case os_type_INTEGER:
171
ser_int_set((int) val, &cur, buf, len);
175
ser_string_set((char *) val, &cur, buf, len);
179
nad_print((nad_t) val, 0, &xml, &xlen);
180
xmlstr = (char *) malloc(sizeof(char) * (xlen + 1));
181
sprintf(xmlstr, "%.*s", xlen, xml);
182
ser_string_set(xmlstr, &cur, buf, len);
186
case os_type_UNKNOWN:
189
} while(os_object_iter_next(o));
194
static os_object_t _st_db_object_deserialise(st_driver_t drv, os_t os, const char *buf, int len) {
202
log_debug(ZONE, "deserialising object");
204
o = os_object_new(os);
208
if(ser_string_get(&key, &cur, buf, len) != 0 || ser_int_get(&ot, &cur, buf, len) != 0) {
209
log_debug(ZONE, "ran off the end of the buffer");
213
log_debug(ZONE, "deserialising key %s", key);
215
switch((os_type_t) ot) {
216
case os_type_BOOLEAN:
217
ser_int_get(&ival, &cur, buf, len);
219
os_object_put(o, key, &ival, os_type_BOOLEAN);
222
case os_type_INTEGER:
223
ser_int_get(&ival, &cur, buf, len);
224
os_object_put(o, key, &ival, os_type_INTEGER);
228
ser_string_get(&sval, &cur, buf, len);
229
os_object_put(o, key, sval, os_type_STRING);
234
ser_string_get(&sval, &cur, buf, len);
235
nad = nad_parse(drv->st->sm->router->nad_cache, sval, strlen(sval));
238
log_write(drv->st->sm->log, LOG_ERR, "db: unable to parse stored XML - database corruption?");
241
os_object_put(o, key, nad, os_type_NAD);
245
case os_type_UNKNOWN:
255
static st_ret_t _st_db_put_guts(st_driver_t drv, const char *type, const char *owner, os_t os, dbdata_t dbd, DBC *c, DB_TXN *t) {
261
memset(&key, 0, sizeof(DBT));
262
memset(&val, 0, sizeof(DBT));
264
key.data = (char *) owner;
265
key.size = strlen(owner);
267
if(os_iter_first(os))
269
o = os_iter_object(os);
270
_st_db_object_serialise(o, &buf, &len);
275
if((err = c->c_put(c, &key, &val, DB_KEYLAST)) != 0) {
276
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't store value for type %s owner %s in storage db: %s", type, owner, db_strerror(err));
283
} while(os_iter_next(os));
288
static st_ret_t _st_db_put(st_driver_t drv, const char *type, const char *owner, os_t os) {
289
drvdata_t data = (drvdata_t) drv->private;
290
dbdata_t dbd = xhash_get(data->dbs, type);
295
if(os_count(os) == 0)
298
ret = _st_db_cursor_new(drv, dbd, &c, &t);
299
if(ret != st_SUCCESS)
302
ret = _st_db_put_guts(drv, type, owner, os, dbd, c, t);
303
if(ret != st_SUCCESS) {
305
_st_db_cursor_free(drv, dbd, c, NULL);
309
return _st_db_cursor_free(drv, dbd, c, t);
312
static st_ret_t _st_db_get(st_driver_t drv, const char *type, const char *owner, const char *filter, os_t *os) {
313
drvdata_t data = (drvdata_t) drv->private;
314
dbdata_t dbd = xhash_get(data->dbs, type);
324
ret = _st_db_cursor_new(drv, dbd, &c, &t);
325
if(ret != st_SUCCESS)
330
f = xhash_get(data->filters, filter);
332
f = storage_filter(filter);
333
cfilter = pstrdup(xhash_pool(data->filters), filter);
334
xhash_put(data->filters, cfilter, (void *) f);
335
pool_cleanup(xhash_pool(data->filters), (pool_cleanup_t) pool_free, f->p);
339
memset(&key, 0, sizeof(DBT));
340
memset(&val, 0, sizeof(DBT));
342
key.data = (char *) owner;
343
key.size = strlen(owner);
347
err = c->c_get(c, &key, &val, DB_SET);
349
o = _st_db_object_deserialise(drv, *os, val.data, val.size);
351
if(o != NULL && !storage_match(f, o, *os))
354
err = c->c_get(c, &key, &val, DB_NEXT_DUP);
357
if(err != 0 && err != DB_NOTFOUND) {
358
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't move cursor for type %s owner %s in storage db: %s", type, owner, db_strerror(err));
360
_st_db_cursor_free(drv, dbd, c, NULL);
365
ret = _st_db_cursor_free(drv, dbd, c, t);
366
if(ret != st_SUCCESS) {
371
if(os_count(*os) == 0) {
379
static st_ret_t _st_db_delete_guts(st_driver_t drv, const char *type, const char *owner, const char *filter, dbdata_t dbd, DBC *c, DB_TXN *t) {
380
drvdata_t data = (drvdata_t) drv->private;
390
f = xhash_get(data->filters, filter);
392
f = storage_filter(filter);
393
cfilter = pstrdup(xhash_pool(data->filters), filter);
394
xhash_put(data->filters, cfilter, (void *) f);
395
pool_cleanup(xhash_pool(data->filters), (pool_cleanup_t) pool_free, f->p);
399
memset(&key, 0, sizeof(DBT));
400
memset(&val, 0, sizeof(DBT));
402
key.data = (char *) owner;
403
key.size = strlen(owner);
407
err = c->c_get(c, &key, &val, DB_SET);
409
o = _st_db_object_deserialise(drv, os, val.data, val.size);
411
if(o != NULL && storage_match(f, o, os))
412
err = c->c_del(c, 0);
415
err = c->c_get(c, &key, &val, DB_NEXT_DUP);
420
if(err != 0 && err != DB_NOTFOUND) {
421
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't move cursor for type %s owner %s in storage db: %s", type, owner, db_strerror(err));
428
static st_ret_t _st_db_delete(st_driver_t drv, const char *type, const char *owner, const char *filter) {
429
drvdata_t data = (drvdata_t) drv->private;
430
dbdata_t dbd = xhash_get(data->dbs, type);
435
ret = _st_db_cursor_new(drv, dbd, &c, &t);
436
if(ret != st_SUCCESS)
439
ret = _st_db_delete_guts(drv, type, owner, filter, dbd, c, t);
440
if(ret != st_SUCCESS) {
442
_st_db_cursor_free(drv, dbd, c, NULL);
446
return _st_db_cursor_free(drv, dbd, c, t);
449
static st_ret_t _st_db_replace(st_driver_t drv, const char *type, const char *owner, const char *filter, os_t os) {
450
drvdata_t data = (drvdata_t) drv->private;
451
dbdata_t dbd = xhash_get(data->dbs, type);
456
ret = _st_db_cursor_new(drv, dbd, &c, &t);
457
if(ret != st_SUCCESS)
460
ret = _st_db_delete_guts(drv, type, owner, filter, dbd, c, t);
461
if(ret != st_SUCCESS) {
463
_st_db_cursor_free(drv, dbd, c, NULL);
467
if(os_count(os) == 0)
468
return _st_db_cursor_free(drv, dbd, c, t);
470
ret = _st_db_put_guts(drv, type, owner, os, dbd, c, t);
471
if(ret != st_SUCCESS) {
473
_st_db_cursor_free(drv, dbd, c, NULL);
477
return _st_db_cursor_free(drv, dbd, c, t);
480
static void _st_db_free(st_driver_t drv) {
481
drvdata_t data = (drvdata_t) drv->private;
488
if(xhash_iter_first(data->dbs))
490
xhash_iter_get(data->dbs, &key, xhv.val);
492
log_debug(ZONE, "closing %s db", key);
494
dbd->db->close(dbd->db, 0);
496
} while(xhash_iter_next(data->dbs));
498
xhash_free(data->dbs);
500
xhash_free(data->filters);
502
data->env->close(data->env, 0);
504
/* remove db environment files if no longer in use */
505
if (db_env_create(&env, 0) == 0)
506
env->remove(env, data->path, 0);
511
/** panic function */
512
static void _st_db_panic(DB_ENV *env, int errval) {
513
log_t log = (log_t) env->app_private;
515
log_write(log, LOG_CRIT, "db: corruption detected! close all jabberd processes and run db_recover");
520
st_ret_t st_init(st_driver_t drv) {
526
path = config_get_one(drv->st->sm->config, "storage.db.path", 0);
528
log_write(drv->st->sm->log, LOG_ERR, "db: no path specified in config file");
532
if((err = db_env_create(&env, 0)) != 0) {
533
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't create environment: %s", db_strerror(err));
537
if((err = env->set_paniccall(env, _st_db_panic)) != 0) {
538
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't set panic call: %s", db_strerror(err));
542
/* store the log context in case we panic */
543
env->app_private = drv->st->sm->log;
545
if((err = env->open(env, path, DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_TXN | DB_CREATE, 0)) != 0) {
546
log_write(drv->st->sm->log, LOG_ERR, "db: couldn't open environment: %s", db_strerror(err));
551
data = (drvdata_t) calloc(1, sizeof(struct drvdata_st));
556
if(config_get_one(drv->st->sm->config, "storage.db.sync", 0) != NULL)
559
data->dbs = xhash_new(101);
561
data->filters = xhash_new(17);
563
drv->private = (void *) data;
565
drv->add_type = _st_db_add_type;
566
drv->put = _st_db_put;
567
drv->get = _st_db_get;
568
drv->replace = _st_db_replace;
569
drv->delete = _st_db_delete;
570
drv->free = _st_db_free;