Browse Source

Merge branch janakj/bdb into master.

* commit 'sr/janakj/bdb': (97 commits)
  Integrate module interfaces.
  Include bdb_mod.h and remove variables available there from the file.
  Various functions prefixed with km_ to avoid conflicts.
  New header file bdb_mod.h with parameters defined in bdb_mod.c
  Comment out MODULE_VERSION in km_db_berkeley.c
  Make km_sources compile within the db_berkeley sip-router module.
  Link the module with libkmi.
  Add km_prefix to locally included header files.
  Defines protecting header fields changed to match filenames.
  Remoted km_Makefile and km_README (same as README)
  Link the modules also with libsrdb1
  Make the module compile in the sip-router source tree.
  Fixed wrong order of directives in modules Makefile.
  Database flags renamed from DB_* to SRDB_* to avoid conflicts.
  Module db_berkeley updated to database api version 2.0.
  - unify common rows and row allocation functionality in the DB API core
  - unify common rows and row allocation functionality in the DB API core
  - fix compilation on OpenBSD, related to missing includes paths and wrong
  - fix compilation on OpenBSD, related to missing includes paths and wrong
  - disable big integer (DB_BIGINT) support for non SQL DB modules for now
  ...
Jan Janak 16 years ago
parent
commit
728a5e6280

+ 22 - 0
modules/db_berkeley/Makefile

@@ -0,0 +1,22 @@
+# $Id:  $
+#
+# example module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+include ../../Makefile.defs 
+auto_gen=
+NAME=db_berkeley.so
+
+# extra debug messages
+# -DBDB_EXTRA_DEBUG is optional
+DEFS +=-DSER_MOD_INTERFACE -I$(LOCALBASE)/include -I$(LOCALBASE)/BerkeleyDB.4.6/include \
+	-I$(SYSBASE)/include
+
+
+LIBS=-L$(LOCALBASE)/lib -L$(SYSBASE)/lib -L$(LOCALBASE)/BerkeleyDB.4.6/lib -ldb
+
+SERLIBPATH=../../lib
+SER_LIBS=$(SERLIBPATH)/srdb2/srdb2 $(SERLIBPATH)/srdb1/srdb1 $(SERLIBPATH)/kmi/kmi
+
+include ../../Makefile.modules

+ 481 - 0
modules/db_berkeley/README

@@ -0,0 +1,481 @@
+Berkeley DB Module
+
+Will Quan
+
+   Cisco Systems
+
+Edited by
+
+Will Quan
+
+   Copyright © 2007 Cisco Systems
+   Revision History
+   Revision $Revision: 846 $ $Date: 2006-05-22 09:15:40 -0500
+                             (Mon, 22 May 2006) $
+     __________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. Kamailio Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. auto_reload (integer)
+              1.3.2. log_enable (integer)
+              1.3.3. journal_roll_interval (integer seconds)
+
+        1.4. Exported Functions
+        1.5. Exported MI Functions
+
+              1.5.1. bdb_reload
+
+        1.6. Installation and Running
+        1.7. Database Schema and Metadata
+        1.8. METADATA_COLUMNS (required)
+        1.9. METADATA_KEYS (required)
+        1.10. METADATA_READONLY (optional)
+        1.11. METADATA_LOGFLAGS (optional)
+        1.12. DB Maintaince Script : kamdbctl
+        1.13. DB Recovery : kambdb_recover
+        1.14. Known Limitations
+
+   List of Examples
+
+   1.1. Set auto_reload parameter
+   1.2. Set log_enable parameter
+   1.3. Set journal_roll_interval parameter
+   1.4. METADATA_COLUMNS
+   1.5. contents of version table
+   1.6. METADATA_COLUMNS
+   1.7. METADATA_KEYS
+   1.8. METADATA_LOGFLAGS
+   1.9. kamdbctl
+   1.10. kambdb_recover usage
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   This is a module which integrates the Berkeley DB into
+   Kamailio. It implements the DB API defined in Kamailio.
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * No dependencies on other Kamailio modules.
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running Kamailio with this module loaded:
+     * Berkeley Berkeley DB 4.6 - an embedded database.
+
+1.3. Exported Parameters
+
+1.3.1. auto_reload (integer)
+
+   The auto-reload will close and reopen a Berkeley DB when the
+   files inode has changed. The operation occurs only duing a
+   query. Other operations such as insert or delete, do not invoke
+   auto_reload.
+
+   Default value is 0 (1 - on / 0 - off).
+
+   Example 1.1. Set auto_reload parameter
+...
+modparam("db_berkeley", "auto_reload", 1)
+...
+
+1.3.2. log_enable (integer)
+
+   The log_enable boolean controls when to create journal files.
+   The following operations can be journaled: INSERT, UPDATE,
+   DELETE. Other operations such as SELECT, do not. This
+   journaling are required if you need to recover from a corrupt
+   DB file. That is, kambdb_recover requires these to rebuild the
+   db file. If you find this log feature useful, you may also be
+   interested in the METADATA_LOGFLAGS bitfield that each table
+   has. It will allow you to control which operations to journal,
+   and the destination (like syslog, stdout, local-file). Refer to
+   bdblib_log() and documentation on METADATA.
+
+   Default value is 0 (1 - on / 0 - off).
+
+   Example 1.2. Set log_enable parameter
+...
+modparam("db_berkeley", "log_enable", 1)
+...
+
+1.3.3. journal_roll_interval (integer seconds)
+
+   The journal_roll_interval will close and open a new log file.
+   The roll operation occurs only at the end of writing a log, so
+   it is not guaranteed to to roll 'on time'.
+
+   Default value is 0 (off).
+
+   Example 1.3. Set journal_roll_interval parameter
+...
+modparam("db_berkeley", "journal_roll_interval", 3600)
+...
+
+1.4. Exported Functions
+
+   No function exported to be used from configuration file.
+
+1.5. Exported MI Functions
+
+1.5.1. bdb_reload
+
+   Causes db_berkeley module to re-read the contents of specified
+   table (or dbenv). The db_berkeley DB actually loads each table
+   on demand, as opposed to loading all at mod_init time. The
+   bdb_reload operation is implemented as a close followed by a
+   reopen. Note- bdb_reload will fail if a table has not been
+   accessed before (because the close will fail).
+
+   Name: bdb_reload
+
+   Parameters: tablename (or db_path); to reload a particular
+   table provide the tablename as the arguement (eg subscriber);
+   to reload all tables provide the db_path to the db files. The
+   path can be found in kamctlrc DB_PATH variable.
+
+1.6. Installation and Running
+
+   First download, compile and install the Berkeley DB. This is
+   outside the scope of this document. Documentation for this
+   procedure is available on the Internet.
+
+   Next, prepare to compile Kamailio with the db_berkeley module.
+   In the directory /modules/db_berkeley, modify the Makefile to
+   point to your distribution of Berkeley DB. You may also define
+   'BDB_EXTRA_DEBUG' to compile in extra debug logs. However, it
+   is not a recommended deployment to production servers.
+
+   Because the module dependes on an external library, the
+   db_berkeley module is not compiled and installed by default.
+   You can use one of the next options.
+     * edit the "Makefile" and remove "db_berkeley" from
+       "excluded_modules" list. Then follow the standard procedure
+       to install Kamailio: "make all; make install".
+     * from command line use: 'make all
+       include_modules="db_berkeley"; make install
+       include_modules="db_berkeley"'.
+
+   Installation of Kamailio is performed by simply running make
+   install as root user of the main directory. This will install
+   the binaries in /usr/local/sbin/. If this was successful,
+   openser control engine files should now be installed as
+   /usr/local/sbin/kamdbctl.
+
+   Decide where (on the filesystem) you want to install the
+   Berkeley DB files. For instance,
+   '/usr/local/etc/kamailio/db_berkeley' directory. Make note of
+   this directory as we need to add this path to the kamctlrc
+   file. Note: Kamailio will not startup without these DB files.
+
+   Edit kamctlrc - There are two parameters in this file that
+   should be configured before openserctrdb script can work
+   properly: DBENGINE and DB_PATH. Edit file:
+   '/usr/local/etc/kamailio/kamctlrc'
+                ## database type: MYSQL, PGSQL, DB_BERKELEY, or DBTEXT,
+by default none is loaded
+                # DBENGINE=DB_BERKELEY
+
+                ## database path used by dbtext or db_berkeley
+                # DB_PATH="/usr/local/etc/kamailio/db_berkeley"
+
+   (Optional) Pre creation step- Customize your meta-data. The DB
+   files are initially seeded with necessary meta-data. This is a
+   good time to review the meta-data section details, before
+   making modifications to your tables dbschema. By default, the
+   files are installed in
+   '/usr/local/share/kamailio/db_berkeley/openser' By default
+   these tables are created Read/Write and without any journalling
+   as shown. These settings can be modified on a per table basis.
+   Note: If you plan to use kambdb_recover, you must change the
+   LOGFLAGS.
+                METADATA_READONLY
+                0
+                METADATA_LOGFLAGS
+                0
+
+   Execute kamdbctl - There are three (3) groups of tables you may
+   need depending on your situation.
+                kamdbctl create                 (required)
+                kamdbctl presence               (optional)
+                kamdbctl extra                  (optional)
+
+   Modify the Kamailio configuration file to use db_berkeley
+   module. The database URL for modules must be the path to the
+   directory where the Berkeley DB table-files are located,
+   prefixed by "berkeley://", e.g.,
+   "berkeley:///usr/local/etc/kamailio/db_berkeley".
+
+   A couple other IMPORTANT things to consider are the 'db_mode'
+   and the 'use_domain' modparams. The description of these
+   parameters are found in usrloc documentation.
+
+   Note on db_mode- The db_berkeley module will only journal the
+   moment usrloc writes back to the DB. The safest mode is mode 3
+   , since the db_berkeley journal files will always be
+   up-to-date. The main point is the db_mode vs. recovery by
+   journal file interaction. Writing journal entries is 'best
+   effort'. So if the hard drive becomes full, the attempt to
+   write a journal entry may fail.
+
+   Note on use_domain- The db_berkeley module will attempt natural
+   joins when performing a query. This is basically a
+   lexigraphical string compare using the keys provided. In most
+   places in the db_berkeley dbschema (unless you customize), the
+   domainname is identified as a natural key. Consider an example
+   where use_domain = 0. In table subscriber, the db will be
+   keying on 'username|NULL' because the default value will be
+   used when that key column is not provided. This effectivly
+   means that later queries must consistently use the username
+   (w.o domain) in order to find a result to that particular
+   subscriber query. The main point is 'use_domain' can not be
+   changed once the db_berkeley is setup.
+
+1.7. Database Schema and Metadata
+
+   All Berkeley DB tables are created via the kamdbctl script.
+   This section provides details as to the content and format of
+   the DB file upon creation.
+
+   Since the Berkeley DB stores key value pairs, the database is
+   seeded with a few meta-data rows . The keys to these rows must
+   begin with 'METADATA'. Here is an example of table meta-data,
+   taken from the table 'version'.
+
+   Note on reserved character- The '|' pipe character is used as a
+   record delimiter within the Berkeley DB implementation and must
+   not be present in any DB field.
+
+   Example 1.4. METADATA_COLUMNS
+METADATA_COLUMNS
+table_name(str) table_version(int)
+METADATA_KEY
+0
+
+   In the above example, the row METADATA_COLUMNS defines the
+   column names and type, and the row METADATA_KEY defines which
+   column(s) form the key. Here the value of 0 indicates that
+   column 0 is the key(ie table_name). With respect to column
+   types, the db_berkeley modules only has the following types:
+   string, str, int, double, and datetime. The default type is
+   string, and is used when one of the others is not specified.
+   The columns of the meta-data are delimited by whitespace.
+
+   The actual column data is stored as a string value, and
+   delimited by the '|' pipe character. Since the code tokenizes
+   on this delimiter, it is important that this character not
+   appear in any valid data field. The following is the output of
+   the 'db_berkeley.sh dump version' command. It shows contents of
+   table 'version' in plain text.
+
+   Example 1.5. contents of version table
+VERSION=3
+format=print
+type=hash
+h_nelem=21
+db_pagesize=4096
+HEADER=END
+ METADATA_READONLY
+ 1
+ address|
+ address|3
+ aliases|
+ aliases|1004
+ dbaliases|
+ dbaliases|1
+ domain|
+ domain|1
+ gw_grp|
+ gw_grp|1
+ gw|
+ gw|4
+ speed_dial|
+ speed_dial|2
+ subscriber|
+ subscriber|6
+ uri|
+ uri|1
+ METADATA_COLUMNS
+ table_name(str) table_version(int)
+ METADATA_KEY
+ 0
+ acc|
+ acc|4
+ grp|
+ grp|2
+ lcr|
+ lcr|2
+ location|
+ location|1004
+ missed_calls|
+ missed_calls|3
+ re_grp|
+ re_grp|1
+ silo|
+ silo|5
+ trusted|
+ trusted|4
+ usr_preferences|
+ usr_preferences|2
+DATA=END
+
+1.8. METADATA_COLUMNS (required)
+
+   The METADATA_COLUMNS row contains the column names and types.
+   Each is space delimited. Here is an example of the data taken
+   from table subscriber :
+
+   Example 1.6. METADATA_COLUMNS
+METADATA_COLUMNS
+username(str) domain(str) password(str) ha1(str) ha1b(str) first_name(st
+r) last_name(str) email_address(str) datetime_created(datetime) timezone
+(str) rpid(str)
+
+   Related (hardcoded) limitations:
+     * maximum of 32 columns per table.
+     * maximum tablename size is 64.
+     * maximum data length is 2048
+
+   Currently supporting these five types: str, datetime, int,
+   double, string.
+
+1.9. METADATA_KEYS (required)
+
+   The METADATA_KEYS row indicates the indexes of the key columns,
+   with respect to the order specified in METADATA_COLUMNS. Here
+   is an example taken from table subscriber that brings up a good
+   point:
+
+   Example 1.7. METADATA_KEYS
+ METADATA_KEY
+ 0 1
+
+   The point is that both the username and domain name are require
+   as the key to this record. Thus, usrloc modparam use_domain = 1
+   must be set for this to work.
+
+1.10. METADATA_READONLY (optional)
+
+   The METADATA_READONLY row contains a boolean 0 or 1. By
+   default, its value is 0. On startup the DB will open initially
+   as read-write (loads metadata) and then if this is set=1, it
+   will close and reopen as read only (ro). I found this useful
+   because readonly has impacts on the internal db locking etc.
+
+1.11. METADATA_LOGFLAGS (optional)
+
+   The METADATA_LOGFLAGS row contains a bitfield that customizes
+   the journaling on a per table basis. If not present the default
+   value is taken as 0. Here are the masks so far (taken from
+   bdb_lib.h):
+
+   Example 1.8. METADATA_LOGFLAGS
+#define JLOG_NONE 0
+#define JLOG_INSERT 1
+#define JLOG_DELETE 2
+#define JLOG_UPDATE 4
+#define JLOG_STDOUT 8
+#define JLOG_SYSLOG 16
+
+   This means that if you want to journal INSERTS to local file
+   and syslog the value should be set to 1+16=17. Or if you do not
+   want to journal at all, set this to 0.
+
+1.12. DB Maintaince Script : kamdbctl
+
+   Use the kamdbctl script for maintaining Kamailio Berkeley DB
+   tables. This script assumes you have DBENGINE and DB_PATH setup
+   correctly in kamctlrc. Note Unsupported commands are- backup,
+   restore, migrate, copy, serweb.
+
+   Example 1.9. kamdbctl
+usage: kamdbctl create
+       kamdbctl presence
+       kamdbctl extra
+       kamdbctl drop
+       kamdbctl reinit
+       kamdbctl bdb list         (lists the underlying db files in DB_PA
+TH)
+       kamdbctl bdb cat       db (prints the contents of db file to STDO
+UT in plain-text)
+       kamdbctl bdb swap      db (installs db.new by db -> db.old; db.ne
+w -> db)
+       kamdbctl bdb append    db datafile (appends data to a new instanc
+e of db; output DB_PATH/db.new)
+       kamdbctl bdb newappend db datafile (appends data to a new instanc
+e of db; output DB_PATH/db.new)
+
+1.13. DB Recovery : kambdb_recover
+
+   The db_berkeley module uses the Concurrent Data Store (CDS)
+   architecture. As such, no transaction or journaling is provided
+   by the DB natively. The application kambdb_recover is
+   specifically written to recover data from journal files that
+   Kamailio creates. The kambdb_recover application requires an
+   additional text file that contains the table schema.
+
+   The schema is loaded with the '-s' option and is required for
+   all operations. Provide the path to the db_berkeley plain-text
+   schema files. By default, these install to
+   '/usr/local/share/kamailio/db_berkeley/kamailio/'.
+
+   The '-h' home option is the DB_PATH path. Unlike the Berkeley
+   utilities, this application does not look for the DB_PATH
+   environment variable, so you have to specify it. If not
+   specified, it will assume the current working directory. The
+   last argument is the operation. There are fundamentally only
+   two operations- create and recover.
+
+   The following illustrates the four operations available to the
+   administrator.
+
+   Example 1.10. kambdb_recover usage
+usage: ./kambdb_recover -s schemadir [-h home] [-c tablename]
+        This will create a brand new DB file with metadata.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-C all]
+        This will create all the core tables, each with metadata.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-r journal-file]
+        This will rebuild a DB and populate it with operation from journ
+al-file.
+        The table name is embedded in the journal-file name by conventio
+n.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-R lastN]
+        This will iterate over all core tables enumerated. If journal fi
+les exist in 'home',
+        a new DB file will be created and populated with the data found
+in the last N files.
+        The files are 'replayed' in chronological order (oldest to newes
+t). This
+        allows the administrator to rebuild the db with a subset of all
+possible
+        operations if needed. For example, you may only be interested in
+
+        the last hours data in table location.
+
+   Important note- A corrupted DB file must be moved out of the
+   way before kambdb_recover is executed.
+
+1.14. Known Limitations
+
+   The Berkeley DB does not nativly support an autoincrement (or
+   sequence) mechanism. Consequently, this version does not
+   support surragate keys in dbschema. These are the id columns in
+   the tables.

+ 572 - 0
modules/db_berkeley/bdb_cmd.c

@@ -0,0 +1,572 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * Implementation of functions related to database commands.
+ */
+
+#include <string.h>
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include "bdb_cmd.h"
+#include "bdb_fld.h"
+#include "bdb_con.h"
+#include "bdb_uri.h"
+#include "bdb_res.h"
+#include "bdb_lib.h"
+
+#define BDB_BUF_SIZE 1024
+
+/** Destroys a bdb_cmd structure.
+ * This function frees all memory used by ld_cmd structure.
+ * @param cmd A pointer to generic db_cmd command being freed.
+ * @param payload A pointer to ld_cmd structure to be freed.
+ */
+static void bdb_cmd_free(db_cmd_t* cmd, bdb_cmd_t* payload)
+{
+	db_drv_free(&payload->gen);
+	if (payload->dbcp)
+		payload->dbcp->c_close(payload->dbcp);
+	if(payload->skey.s)
+		pkg_free(payload->skey.s);
+	pkg_free(payload);
+}
+
+int bdb_prepare_query(db_cmd_t* cmd, bdb_cmd_t *bcmd)
+{
+	bdb_tcache_t *tbc = NULL;
+	bdb_table_t *tp = NULL;
+	bdb_fld_t *f;
+	db_fld_t *fld;
+	int mode;
+	int i;
+
+	if(bcmd->bcon==NULL || bcmd->bcon->dbp==NULL)
+		return -1;
+	
+	tbc = bdblib_get_table(bcmd->bcon->dbp, &cmd->table);
+	if(tbc==NULL)
+	{
+		ERR("bdb: table does not exist!\n");
+		return -1;
+	}
+
+	tp = tbc->dtp;
+	if(tp==NULL || tp->db==NULL)
+	{
+		ERR("bdb: table not loaded!\n");
+		return -1;
+	}
+
+	mode = 0;
+	if (!DB_FLD_EMPTY(cmd->result)) 
+	{ /* columns to be returned provided */
+		if (cmd->result_count > tp->ncols)
+		{
+			ERR("bdb: too many columns in query\n");
+			goto error;
+		}
+	} else {
+		mode = 1;
+		cmd->result = db_fld(tp->ncols + 1);
+		cmd->result_count = tp->ncols;
+		for(i = 0; i < cmd->result_count; i++) {
+			if (bdb_fld(cmd->result + i, cmd->table.s) < 0)
+				goto error;
+		}		
+	}
+	
+	for (i = 0; i < cmd->result_count; i++) {
+		fld = cmd->result + i;
+		f = DB_GET_PAYLOAD(fld);
+		if(mode==1)
+		{
+			DBG("bdb: column name [%.*s]\n", tp->colp[i]->name.len,
+					tp->colp[i]->name.s);
+		
+			f->name = pkg_malloc(tp->colp[i]->name.len+1);
+			if (f->name == NULL) {
+				ERR("bdb: Out of private memory\n");
+				goto error;
+			}
+			strncpy(f->name, tp->colp[i]->name.s, tp->colp[i]->name.len);
+			f->name[tp->colp[i]->name.len] = '\0';
+			fld->name = f->name;
+			fld->type = tp->colp[i]->type;
+			f->col_pos = i;
+		} else {
+			f->col_pos = bdb_get_colpos(tp, fld->name);
+			if(f->col_pos == -1)
+			{
+				ERR("bdb: Column not found\n");
+				goto error;
+			}
+		}
+		switch(fld->type) {
+			case DB_INT:
+			case DB_BITMAP:
+			case DB_FLOAT:
+			case DB_DOUBLE:
+			case DB_DATETIME:
+			case DB_STR:
+				if (!f->buf.s) f->buf.s = pkg_malloc(BDB_BUF_SIZE);
+				if (f->buf.s == NULL) {
+					ERR("bdb: No memory left\n");
+					goto error;
+				}
+				fld[i].v.lstr.s = f->buf.s;
+			break;
+
+			case DB_CSTR:
+				if (!f->buf.s) f->buf.s = pkg_malloc(BDB_BUF_SIZE);
+				if (f->buf.s == NULL) {
+					ERR("bdb: No memory left\n");
+					goto error;
+				}
+				fld[i].v.cstr = f->buf.s;
+			break;
+
+		case DB_BLOB:
+				if (!f->buf.s) f->buf.s = pkg_malloc(BDB_BUF_SIZE);
+				if (f->buf.s == NULL) {
+					ERR("mysql: No memory left\n");
+					goto error;
+				}
+				fld[i].v.blob.s = f->buf.s;
+			break;
+
+		case DB_NONE:
+			/* Eliminates gcc warning */
+			break;
+		}
+	}
+	
+	if (!DB_FLD_EMPTY(cmd->match))
+	{
+		if (cmd->match_count > tp->ncols)
+		{
+			ERR("bdb: too many columns in match struct of query\n");
+			goto error;
+		}
+		for (i = 0; i < cmd->match_count; i++) {
+			fld = cmd->result + i;
+			f = DB_GET_PAYLOAD(fld);
+			f->col_pos = bdb_get_colpos(tp, fld->name);
+			if(f->col_pos == -1)
+			{
+				ERR("bdb: Match column not found\n");
+				goto error;
+			}
+		}
+	}
+
+	return 0;
+
+error:
+	return -1;
+}
+
+int bdb_query(db_cmd_t* cmd, bdb_cmd_t *bcmd)
+{
+	DBT key;
+	DB *db;
+	static char kbuf[MAX_ROW_SIZE];
+	int klen;
+
+	bdb_tcache_t *tbc = NULL;
+	bdb_table_t *tp = NULL;
+
+	if(bcmd->bcon==NULL || bcmd->bcon->dbp==NULL)
+		return -1;
+	
+	tbc = bdblib_get_table(bcmd->bcon->dbp, &cmd->table);
+	if(tbc==NULL)
+	{
+		ERR("bdb: table does not exist!\n");
+		return -1;
+	}
+
+	tp = tbc->dtp;
+	if(tp==NULL)
+	{
+		ERR("bdb: table not loaded!\n");
+		return -1;
+	}
+	db = tp->db;
+	if(db==NULL)
+	{
+		ERR("bdb: db structure not intialized!\n");
+		return -1;
+	}
+
+	if (DB_FLD_EMPTY(cmd->match))
+	{ /* no match constraint */
+		if (db->cursor(db, NULL, &bcmd->dbcp, 0) != 0) 
+		{
+			ERR("bdb: error creating cursor\n");
+			goto error;
+		}
+		bcmd->skey.len = 0;
+		return 0;
+	}
+
+	memset(&key, 0, sizeof(DBT));
+	memset(kbuf, 0, MAX_ROW_SIZE);
+	
+	klen=MAX_ROW_SIZE;
+	if(bdblib_valtochar(tp, cmd->match, cmd->match_count,
+			kbuf, &klen, BDB_KEY)!=0)
+	{
+		ERR("bdb: error creating key\n");
+		goto error;
+	}
+	
+	if(klen > bcmd->skey_size || bcmd->skey.s==NULL)
+	{
+		if(bcmd->skey.s!=NULL)
+			pkg_free(bcmd->skey.s);
+		bcmd->skey.s = (char*)pkg_malloc(klen*sizeof(char));
+		if(bcmd->skey.s == NULL)
+		{
+			ERR("bdb: no pkg memory\n");
+			goto error;
+		}
+		bcmd->skey_size = klen;
+	}
+	memcpy(bcmd->skey.s, kbuf, klen);
+	bcmd->skey.len = klen;
+
+	return 0;
+error:
+	return -1;
+}
+
+int bdb_cmd(db_cmd_t* cmd)
+{
+	bdb_cmd_t *bcmd;
+	db_con_t  *con;
+	bdb_con_t *bcon;
+
+	bcmd = (bdb_cmd_t*)pkg_malloc(sizeof(bdb_cmd_t));
+	if (bcmd == NULL) {
+		ERR("bdb: No memory left\n");
+		goto error;
+	}
+	memset(bcmd, '\0', sizeof(bdb_cmd_t));
+	if (db_drv_init(&bcmd->gen, bdb_cmd_free) < 0) goto error;
+
+	con = cmd->ctx->con[db_payload_idx];
+	bcon = DB_GET_PAYLOAD(con);
+	bcmd->bcon = bcon;
+
+	switch(cmd->type) {
+	case DB_PUT:
+	case DB_DEL:
+	case DB_UPD:
+		ERR("bdb: The driver does not support DB modifications yet.\n");
+		goto error;
+		break;
+
+	case DB_GET:
+		if(bdb_prepare_query(cmd, bcmd)!=0)
+		{
+			ERR("bdb: error preparing query.\n");
+			goto error;
+		}
+		break;
+
+	case DB_SQL:
+		ERR("bdb: The driver does not support raw queries yet.\n");
+		goto error;
+	}
+
+	DB_SET_PAYLOAD(cmd, bcmd);
+	return 0;
+
+error:
+	if (bcmd) {
+		DB_SET_PAYLOAD(cmd, NULL);
+		db_drv_free(&bcmd->gen);
+		pkg_free(bcmd);
+	}
+	return -1;
+}
+
+
+int bdb_cmd_exec(db_res_t* res, db_cmd_t* cmd)
+{
+	db_con_t* con;
+	bdb_cmd_t *bcmd;
+	bdb_con_t *bcon;
+
+	/* First things first: retrieve connection info from the currently active
+	 * connection and also mysql payload from the database command
+	 */
+	con = cmd->ctx->con[db_payload_idx];
+	bcmd = DB_GET_PAYLOAD(cmd);
+	bcon = DB_GET_PAYLOAD(con);
+
+	if ((bcon->flags & BDB_CONNECTED)==0) {
+		ERR("bdb: not connected\n");
+		return -1;
+	}
+	bcmd->next_flag = -1;
+	switch(cmd->type) {
+		case DB_DEL:
+		case DB_PUT:
+		case DB_UPD:
+				/* no result expected - cleanup */
+				DBG("bdb: query with no result.\n");
+			break;
+		case DB_GET:
+				return bdb_query(cmd, bcmd);
+			break;
+		default:
+				/* result expected - no cleanup */
+				DBG("bdb: query with result.\n");
+	}
+
+	return 0;
+}
+
+int bdb_update_result(db_cmd_t *cmd, DBT *data)
+{
+	bdb_fld_t *f;
+	db_fld_t *fld;
+	int i;
+	int col;
+	char *s;
+	static str col_map[MAX_NUM_COLS];
+
+	memset(col_map, 0, MAX_NUM_COLS*sizeof(str));
+
+	col = 0;
+	s = (char*)data->data;
+	col_map[col].s = s;
+	while(*s!='\0')
+	{
+		if(*s == *DELIM)
+		{
+			col_map[col].len = s - col_map[col].s;
+			col++;
+			col_map[col].s = s+1;
+		}
+		s++;
+	}
+	col_map[col].len = s - col_map[col].s;
+
+	for (i = 0; i < cmd->result_count; i++) {
+		fld = cmd->result + i;
+		f = DB_GET_PAYLOAD(fld);
+		if(col_map[f->col_pos].len == 0)
+		{
+			fld->flags |= DB_NULL;
+			continue;
+		}
+		fld->flags &= ~DB_NULL;
+
+		switch(fld->type) {
+			case DB_STR:
+				fld->v.lstr.s = f->buf.s;
+				if(col_map[f->col_pos].len < BDB_BUF_SIZE)
+				{
+					fld->v.lstr.len = col_map[f->col_pos].len;
+				} else {
+					/* truncate ?!? */
+					fld->v.lstr.len = BDB_BUF_SIZE - 1;
+				}
+				memcpy(fld->v.lstr.s, col_map[f->col_pos].s, fld->v.lstr.len);
+			break;
+
+			case DB_BLOB:
+				fld->v.blob.s = f->buf.s;
+				if(col_map[f->col_pos].len < BDB_BUF_SIZE)
+				{
+					fld->v.blob.len = col_map[f->col_pos].len;
+				} else {
+					/* truncate ?!? */
+					fld->v.blob.len = BDB_BUF_SIZE - 1;
+				}
+				memcpy(fld->v.blob.s, col_map[f->col_pos].s, fld->v.blob.len);
+
+			break;
+
+			case DB_CSTR:
+				fld->v.cstr = f->buf.s;
+				if(col_map[f->col_pos].len < BDB_BUF_SIZE)
+				{
+					memcpy(fld->v.cstr, col_map[f->col_pos].s,
+							col_map[f->col_pos].len);
+					fld->v.cstr[col_map[f->col_pos].len] = '\0';
+				} else {
+					/* truncate ?!? */
+					memcpy(fld->v.cstr, col_map[f->col_pos].s,
+							BDB_BUF_SIZE - 1);
+					fld->v.cstr[BDB_BUF_SIZE - 1] = '\0';;
+				}
+
+			break;
+			
+			case DB_DATETIME:
+				/* str to time */
+				col_map[f->col_pos].s[col_map[f->col_pos].len]='\0';
+				if (bdb_str2time(col_map[f->col_pos].s, &fld->v.time) < 0)
+				{
+					ERR("Error while converting INT value from string\n");
+					return -1;
+				}
+			break;
+
+			case DB_INT:
+				/* str to int */
+				col_map[f->col_pos].s[col_map[f->col_pos].len]='\0';
+				if (bdb_str2int(col_map[f->col_pos].s, &fld->v.int4) < 0)
+				{
+					ERR("Error while converting INT value from string\n");
+					return -1;
+				}
+			break;
+
+			case DB_FLOAT:
+			case DB_DOUBLE:
+				/* str to dowuble */
+				col_map[f->col_pos].s[col_map[f->col_pos].len]='\0';
+				if (bdb_str2double(col_map[f->col_pos].s, &fld->v.dbl) < 0)
+				{
+					ERR("Error while converting DOUBLE value from string\n");
+					return -1;
+				}
+			break;
+
+			case DB_BITMAP:
+				/* str to int */
+				col_map[f->col_pos].s[col_map[f->col_pos].len]='\0';
+				if (bdb_str2int(col_map[f->col_pos].s, &fld->v.int4) < 0)
+				{
+					ERR("Error while converting BITMAP value from string\n");
+					return -1;
+				}
+			break;
+
+			case DB_NONE:
+			break;
+		}
+	}
+	return 0;
+
+}
+
+int bdb_cmd_first(db_res_t* res)
+{
+	bdb_cmd_t *bcmd;
+
+	bcmd = DB_GET_PAYLOAD(res->cmd);
+	switch (bcmd->next_flag) {
+		case -2: /* table is empty */
+			return 1;
+		case 0:  /* cursor position is 0 */
+			return 0;
+		case 1:  /* next row */
+		case 2:  /* EOF */
+			ERR("bdb: no next row.\n");
+			return -1;
+		default:
+			return bdb_cmd_next(res);
+	}
+}
+
+
+int bdb_cmd_next(db_res_t* res)
+{
+	db_con_t *con;
+	bdb_res_t *bres;
+	bdb_con_t *bcon;
+	bdb_cmd_t *bcmd;
+	DBT key, data;
+	int ret;
+	static char dbuf[MAX_ROW_SIZE];
+
+	con = res->cmd->ctx->con[db_payload_idx];
+	bres = DB_GET_PAYLOAD(res);
+	bcon = DB_GET_PAYLOAD(con);
+	bcmd = DB_GET_PAYLOAD(res->cmd);
+
+	if (bcmd->next_flag == 2 || bcmd->next_flag == -2) return 1;
+
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+
+	ret = 0;
+	if(bcmd->skey.len==0)
+	{
+		while((ret = bcmd->dbcp->c_get(bcmd->dbcp, &key, &data, DB_NEXT))==0)
+		{
+			if(!strncasecmp((char*)key.data,"METADATA",8)) 
+				continue;
+			break;
+		}
+		if(ret!=0)
+		{
+			bcmd->next_flag =  bcmd->next_flag<0?-2:2;
+			return 1;
+		}
+	} else {
+		key.data = bcmd->skey.s;
+		key.ulen = bcmd->skey_size;
+		key.flags = DB_DBT_USERMEM;
+		key.size = bcmd->skey.len;
+		ret = bcmd->dbcp->c_get(bcmd->dbcp, &key, &data, DB_NEXT);
+		if(ret!=0)
+		{
+			bcmd->next_flag = bcmd->next_flag<0?-2:2;
+			return 1;
+		}
+	}
+
+	if (bcmd->next_flag <= 0) {
+		bcmd->next_flag++;
+	}
+
+	if (bdb_update_result(res->cmd, &data) < 0) {
+		return -1;
+	}
+
+	res->cur_rec->fld = res->cmd->result;
+	return 0;
+}
+
+
+/** @} */

+ 93 - 0
modules/db_berkeley/bdb_cmd.h

@@ -0,0 +1,93 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _BDB_CMD_H_
+#define _BDB_CMD_H_
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * Declaration of bdb_cmd data structure that contains BDB specific data
+ * stored in db_cmd structures and related functions.
+ */
+
+#include <stdarg.h>
+#include <sys/time.h>
+#include <db.h>
+
+#include "../../lib/srdb2/db_drv.h"
+#include "../../lib/srdb2/db_cmd.h"
+#include "../../lib/srdb2/db_res.h"
+#include "../../str.h"
+
+#include "bdb_con.h"
+
+/** Extension structure of db_cmd adding BDB specific data.
+ * This data structure extends the generic data structure db_cmd in the
+ * database API with data specific to the ldap driver.
+ */
+typedef struct _bdb_cmd {
+	db_drv_t gen;    /**< Generic part of the data structure (must be first */
+	bdb_con_t *bcon; /**< DB connection handle */
+	DB *dbp;         /**< DB structure handle */
+	DBC *dbcp;       /**< DB cursor handle */
+	int next_flag;
+	str skey;
+	int skey_size;
+} bdb_cmd_t, *bdb_cmd_p;
+
+
+/** Creates a new bdb_cmd data structure.
+ * This function allocates and initializes memory for a new bdb_cmd data
+ * structure. The data structure is then attached to the generic db_cmd
+ * structure in cmd parameter.
+ * @param cmd A generic db_cmd structure to which the newly created bdb_cmd
+ *            structure will be attached.
+ */
+int bdb_cmd(db_cmd_t* cmd);
+
+
+/** The main execution function in BDB SER driver.
+ * This is the main execution function in this driver. It is executed whenever
+ * a SER module calls db_exec and the target database of the commands is
+ * ldap.
+ * @param res A pointer to (optional) result structure if the command returns
+ *            a result.
+ * @retval 0 if executed successfully
+ * @retval A negative number if the database server failed to execute command
+ * @retval A positive number if there was an error on client side (SER)
+ */
+int bdb_cmd_exec(db_res_t* res, db_cmd_t* cmd);
+
+
+int bdb_cmd_first(db_res_t* res);
+
+
+int bdb_cmd_next(db_res_t* res);
+
+/** @} */
+
+#endif /* _BDB_CMD_H_ */

+ 178 - 0
modules/db_berkeley/bdb_con.c

@@ -0,0 +1,178 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * Functions related to connections to BDB.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include "bdb_con.h"
+#include "bdb_uri.h"
+#include "bdb_lib.h"
+
+/** Free all memory allocated for a bdb_con structure.
+ * This function function frees all memory that is in use by
+ * a bdb_con structure.
+ * @param con A generic db_con connection structure.
+ * @param payload BDB specific payload to be freed.
+ */
+static void bdb_con_free(db_con_t* con, bdb_con_t *payload)
+{
+	bdb_uri_t *buri;
+	if (!payload)
+		return;
+
+	buri = DB_GET_PAYLOAD(con->uri);
+
+	/* Delete the structure only if there are no more references
+	 * to it in the connection pool
+	 */
+	if (db_pool_remove((db_pool_entry_t*)payload) == 0) return;
+
+	db_pool_entry_free(&payload->gen);
+
+	/* destroy and free BDB env */
+	pkg_free(payload);
+}
+
+
+int bdb_con(db_con_t* con)
+{
+	bdb_con_t* bcon;
+	bdb_uri_t* buri;
+
+	buri = DB_GET_PAYLOAD(con->uri);
+
+	/* First try to lookup the connection in the connection pool and
+	 * re-use it if a match is found
+	 */
+	bcon = (bdb_con_t*)db_pool_get(con->uri);
+	if (bcon) {
+		DBG("bdb: Connection to %s found in connection pool\n",
+			buri->uri);
+		goto found;
+	}
+
+	bcon = (bdb_con_t*)pkg_malloc(sizeof(bdb_con_t));
+	if (!bcon) {
+		ERR("bdb: No memory left\n");
+		goto error;
+	}
+	memset(bcon, '\0', sizeof(bdb_con_t));
+	if (db_pool_entry_init(&bcon->gen, bdb_con_free, con->uri) < 0) goto error;
+
+	DBG("bdb: Preparing new connection to %s\n", buri->uri);
+	if(bdb_is_database(buri->path.s)!=0)
+	{	
+		ERR("bdb: database [%.*s] does not exists!\n",
+				buri->path.len, buri->path.s);
+		goto error;
+	}
+
+	/* Put the newly created BDB connection into the pool */
+	db_pool_put((struct db_pool_entry*)bcon);
+	DBG("bdb: Connection stored in connection pool\n");
+
+found:
+	/* Attach driver payload to the db_con structure and set connect and
+	 * disconnect functions
+	 */
+	DB_SET_PAYLOAD(con, bcon);
+	con->connect = bdb_con_connect;
+	con->disconnect = bdb_con_disconnect;
+	return 0;
+
+error:
+	if (bcon) {
+		db_pool_entry_free(&bcon->gen);
+		pkg_free(bcon);
+	}
+	return -1;
+}
+
+
+
+int bdb_con_connect(db_con_t* con)
+{
+	bdb_con_t *bcon;
+	bdb_uri_t *buri;
+
+	bcon = DB_GET_PAYLOAD(con);
+	buri = DB_GET_PAYLOAD(con->uri);
+
+	/* Do not reconnect already connected connections */
+	if (bcon->flags & BDB_CONNECTED) return 0;
+
+	DBG("bdb: Connecting to %s\n", buri->uri);
+
+	/* create BDB environment */
+	bcon->dbp = bdblib_get_db(&buri->path);
+	if(bcon->dbp == NULL)
+	{
+		ERR("bdb: error binding to DB %s\n", buri->uri);
+		return -1;
+	}
+
+	DBG("bdb: Successfully bound to %s\n", buri->uri);
+	bcon->flags |= BDB_CONNECTED;
+	return 0;
+
+}
+
+
+void bdb_con_disconnect(db_con_t* con)
+{
+	bdb_con_t *bcon;
+	bdb_uri_t *buri;
+
+	bcon = DB_GET_PAYLOAD(con);
+	buri = DB_GET_PAYLOAD(con->uri);
+
+	if ((bcon->flags & BDB_CONNECTED) == 0) return;
+
+	DBG("bdb: Unbinding from %s\n", buri->uri);
+	if(bcon->dbp==NULL)
+	{
+		bcon->flags &= ~BDB_CONNECTED;
+		return;
+	}
+
+	bdblib_close(bcon->dbp, &buri->path);
+	bcon->dbp = 0;
+
+	bcon->flags &= ~BDB_CONNECTED;
+}
+
+
+/** @} */

+ 92 - 0
modules/db_berkeley/bdb_con.h

@@ -0,0 +1,92 @@
+/* 
+ * $Id$ 
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _BDB_CON_H_
+#define _BDB_CON_H_
+
+/** \addtogroup bdb
+ * @{ 
+ */
+
+/** \file 
+ * Implementation of BDB per-connection related data structures and functions.
+ */
+
+#include <time.h>
+#include <db.h>
+
+#include "../../lib/srdb2/db_pool.h"
+#include "../../lib/srdb2/db_con.h"
+#include "../../lib/srdb2/db_uri.h"
+
+#include "bdb_lib.h"
+
+/** 
+ * Per-connection flags for BDB connections.
+ */
+enum bdb_con_flags {
+	BDB_CONNECTED      = (1 << 0), /**< The connection has been connected successfully */
+};
+
+
+/** A structure representing a connection to a BDB.
+ * This structure represents connections to BDB. It contains
+ * BDB specific per-connection data, 
+ */
+
+typedef struct _bdb_con {
+	db_pool_entry_t gen; /**< Generic part of the structure */
+	bdb_db_t	*dbp;	 /**< DB structure handle */
+	unsigned int flags;	 /**< Flags */
+} bdb_con_t, *bdb_con_p;
+
+/** Create a new bdb_con structure.
+ * This function creates a new bdb_con structure and attachs the structure to
+ * the generic db_con structure in the parameter.
+ * @param con A generic db_con structure to be extended with BDB payload
+ * @retval 0 on success
+ * @retval A negative number on error
+ */
+int bdb_con(db_con_t* con);
+
+
+/** Establish a new connection to server.  
+ * This function is called when a SER module calls db_connect to establish a
+ * new connection to the database server.
+ * @param con A structure representing database connection.
+ * @retval 0 on success.
+ * @retval A negative number on error.
+ */
+int bdb_con_connect(db_con_t* con);
+
+
+/** Disconnected from BDB.
+ * Disconnects a previously connected connection to BDB.
+ * @param con A structure representing the connection to be disconnected.
+ */
+void bdb_con_disconnect(db_con_t* con);
+
+/** @} */
+
+#endif /* _BDB_CON_H_ */

+ 78 - 0
modules/db_berkeley/bdb_fld.c

@@ -0,0 +1,78 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * Data field conversion and type checking functions.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>   /* strptime, XOPEN issue must be >= 4 */
+
+#include "../../lib/srdb2/db_drv.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include "bdb_fld.h"
+
+static void bdb_fld_free(db_fld_t* fld, bdb_fld_t* payload)
+{
+	db_drv_free(&payload->gen);
+	if (payload->buf.s) pkg_free(payload->buf.s);
+	if (payload->name) pkg_free(payload->name);
+	pkg_free(payload);
+}
+
+
+int bdb_fld(db_fld_t* fld, char* table)
+{
+	bdb_fld_t *res;
+
+	res = (bdb_fld_t*)pkg_malloc(sizeof(bdb_fld_t));
+	if (res == NULL) {
+		ERR("oracle: No memory left\n");
+		return -1;
+	}
+	memset(res, '\0', sizeof(bdb_fld_t));
+	res->col_pos = -1;
+	if (db_drv_init(&res->gen, bdb_fld_free) < 0) goto error;
+
+	DB_SET_PAYLOAD(fld, res);
+	return 0;
+
+error:
+	if (res) pkg_free(res);
+	return -1;
+}
+
+
+/** @} */

+ 64 - 0
modules/db_berkeley/bdb_fld.h

@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _BDB_FLD_H_
+#define _BDB_FLD_H_
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * Implementation of bdb_fld data structure representing BDB fields and
+ * related functions.
+ */
+
+#include <db.h>
+
+#include "../../lib/srdb2/db_gen.h"
+#include "../../lib/srdb2/db_fld.h"
+
+typedef struct _bdb_fld {
+	db_drv_t gen;
+	char* name;
+	int is_null;
+	unsigned long length;
+	str buf;
+	int col_pos;
+} bdb_fld_t, *bdb_fld_p;
+
+
+/** Creates a new BDB specific payload.
+ * This function creates a new BDB specific payload structure and
+ * attaches the structure to the generic db_fld structure.
+ * @param fld A generic db_fld structure to be exended.
+ * @param table Name of the table on the server.
+ * @retval 0 on success.
+ * @retval A negative number on error.
+ */
+int bdb_fld(db_fld_t* fld, char* table);
+
+/** @} */
+
+#endif /* _BDB_FLD_H_ */

+ 1468 - 0
modules/db_berkeley/bdb_lib.c

@@ -0,0 +1,1468 @@
+/*
+ * $Id: bdb_lib.c 4585 2008-08-06 08:20:30Z klaus_darilion $
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+
+#include "bdb_fld.h"
+#include "bdb_lib.h"
+
+static bdb_params_p _bdb_parms = NULL;
+
+/**
+ *
+ */
+int bdblib_init(bdb_params_p _p) 
+{
+	bdb_params_p dp = NULL;
+	if (_bdb_parms != NULL)
+		return 0;
+		
+		/*create default parms*/
+	dp = (bdb_params_p) pkg_malloc( sizeof(bdb_params_t) );
+	if (dp==NULL) 
+	{
+		ERR("not enough private memory\n");
+		return -1;
+	}
+		
+	if(_p!=NULL)
+	{
+		dp->cache_size  = _p->cache_size;
+		dp->auto_reload = _p->auto_reload;
+		dp->log_enable  = _p->log_enable;
+		dp->journal_roll_interval = _p->journal_roll_interval;
+	} else {
+		dp->cache_size = (4 * 1024 * 1024); //4Mb
+		dp->auto_reload = 0;
+		dp->log_enable = 0;
+		dp->journal_roll_interval = 3600;
+	}
+		
+	_bdb_parms = dp;
+	return 0;
+}
+
+
+/**
+ * close all DBs and then the DBENV; free all memory
+ */
+int bdblib_destroy(void)
+{
+	if(_bdb_parms)	pkg_free(_bdb_parms);
+	return 0;
+}
+
+
+/** closes the underlying Berkeley DB.
+  assumes the lib data-structures are already initialzed;
+  used to sync and reload the db file.
+*/
+int bdblib_close(bdb_db_p _db_p, str *dirpath)
+{
+	int rc;
+	bdb_tcache_p _tbc;
+	DB* _db = NULL;
+	DB_ENV* _env = NULL;
+	
+	if (_db_p==NULL || dirpath==NULL)
+		return -1;
+	
+	rc = 0;
+	
+	if (_db_p==NULL)
+	{	
+		DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
+		return 1; /*table not found*/
+	}
+	
+	_env = _db_p->dbenv;
+	_tbc = _db_p->tables;
+	DBG("ENV %.*s \n", _db_p->name.len, _db_p->name.s);
+	if(dirpath->len == _db_p->name.len && 
+		!strncasecmp(dirpath->s, _db_p->name.s, _db_p->name.len))
+	{
+		//close the whole dbenv
+		DBG("ENV %.*s \n", dirpath->len, dirpath->s);
+		while(_tbc)
+		{
+			if(_tbc->dtp)
+			{
+				_db = _tbc->dtp->db;
+				if(_db)
+					rc = _db->close(_db, 0);
+				if(rc != 0)
+					ERR("error closing %s\n", _tbc->dtp->name.s);
+				_tbc->dtp->db = NULL;
+			}
+			_tbc = _tbc->next;
+		}
+		_env->close(_env, 0);
+		_db_p->dbenv = NULL;
+		return 0;
+	}
+		
+	//close a particular db
+	while(_tbc)
+	{
+		if(_tbc->dtp)
+		{
+			DBG("checking DB %.*s \n", _tbc->dtp->name.len, _tbc->dtp->name.s);
+				
+			if(_tbc->dtp->name.len == dirpath->len && 
+				!strncasecmp(_tbc->dtp->name.s, dirpath->s, dirpath->len ))
+			{
+				DBG("DB %.*s \n", dirpath->len, dirpath->s);
+				_db = _tbc->dtp->db;
+				if(_db)
+					rc = _db->close(_db, 0);
+				if(rc != 0)
+					ERR("error closing %s\n", _tbc->dtp->name.s);
+				_tbc->dtp->db = NULL;
+				return 0;
+			}
+		}
+		_tbc = _tbc->next;
+	}
+	DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
+	return 1; /*table not found*/	
+}
+
+/** opens the underlying Berkeley DB.
+  assumes the lib data-structures are already initialzed;
+  used to sync and reload the db file.
+*/
+int bdblib_reopen(bdb_db_p _db_p, str *dirpath)
+{
+	int rc, flags;
+	bdb_tcache_p _tbc;
+	DB* _db = NULL;
+	DB_ENV* _env = NULL;
+	rc = flags = 0;
+	_tbc = NULL;
+	
+	if (_db_p==NULL || dirpath==NULL)
+		return -1;
+
+	
+	if (_db_p)
+	{
+		DBG("bdb: DB not found %.*s \n", dirpath->len, dirpath->s);
+		return 1; /*table not found*/
+	}
+	
+	_env = _db_p->dbenv;
+	_tbc = _db_p->tables;
+		
+	if(dirpath->len ==_db_p->name.len && 
+		!strncasecmp(dirpath->s, _db_p->name.s, _db_p->name.len))
+	{
+		//open the whole dbenv
+		DBG("-- bdblib_reopen ENV %.*s \n", dirpath->len, dirpath->s);
+		if(!_db_p->dbenv)
+		{
+			rc = bdblib_create_dbenv(&_env, dirpath->s);
+			_db_p->dbenv = _env;
+		}
+			
+		if(rc!=0) return rc;
+
+		_env = _db_p->dbenv;
+		_tbc = _db_p->tables;
+
+		while(_tbc)
+		{
+			if(_tbc->dtp)
+			{
+				if(!_tbc->dtp->db)
+				{
+					if ((rc = db_create(&_db, _env, 0)) != 0)
+					{
+						_env->err(_env, rc, "db_create");
+						ERR("error in db_create, db error: %s.\n",
+								db_strerror(rc));
+						bdblib_recover(_tbc->dtp, rc);
+					}
+				}
+					
+				if ((rc = _db->open(_db, NULL, dirpath->s, NULL, DB_HASH,
+								DB_CREATE, 0664)) != 0)
+				{
+					_db->dbenv->err(_env, rc, "DB->open: %s", dirpath->s);
+					ERR("error in db_open: %s.\n",db_strerror(rc));
+					bdblib_recover(_tbc->dtp, rc);
+				}
+					
+				_tbc->dtp->db = _db;
+			}
+			_tbc = _tbc->next;
+		}
+		_env->close(_env, 0);
+		return rc;
+	}
+		
+	// open a particular db
+	while(_tbc)
+	{
+		if(_tbc->dtp)
+		{
+			ERR("checking DB %.*s \n", _tbc->dtp->name.len, _tbc->dtp->name.s);
+				
+			if(_tbc->dtp->name.len == dirpath->len && 
+				!strncasecmp(_tbc->dtp->name.s, dirpath->s, dirpath->len ))
+			{
+				ERR("DB %.*s \n", dirpath->len, dirpath->s);
+				if(!_tbc->dtp->db) 
+				{
+					if ((rc = db_create(&_db, _env, 0)) != 0)
+					{
+						_env->err(_env, rc, "db_create");
+						ERR("error in db_create, db error: %s.\n",
+								db_strerror(rc));
+						bdblib_recover(_tbc->dtp, rc);
+					}
+				}
+					
+				if ((rc = _db->open(_db, NULL, dirpath->s, NULL, DB_HASH,
+								DB_CREATE, 0664)) != 0)
+				{
+					_db->dbenv->err(_env, rc, "DB->open: %s", dirpath->s);
+					ERR("bdb open: %s.\n",db_strerror(rc));
+					bdblib_recover(_tbc->dtp, rc);
+				}
+				_tbc->dtp->db = _db;
+				return rc;
+			}
+		}
+		_tbc = _tbc->next;
+	}
+
+	DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
+	return 1; /*table not found*/
+}
+
+
+/**
+ *
+ */
+int bdblib_create_dbenv(DB_ENV **_dbenv, char* _home)
+{
+	DB_ENV *env;
+	char *progname;
+	int rc, flags;
+	
+	progname = "openser";
+	
+	/* Create an environment and initialize it for additional error * reporting. */ 
+	if ((rc = db_env_create(&env, 0)) != 0) 
+	{
+		ERR("db_env_create failed! bdb error: %s.\n", db_strerror(rc)); 
+		return (rc);
+	}
+ 
+	env->set_errpfx(env, progname);
+
+	/*  Specify the shared memory buffer pool cachesize */ 
+	if ((rc = env->set_cachesize(env, 0, _bdb_parms->cache_size, 0)) != 0) 
+	{
+		ERR("dbenv set_cachsize failed! bdb error: %s.\n", db_strerror(rc));
+		env->err(env, rc, "set_cachesize"); 
+		goto err; 
+	}
+
+	/* Concurrent Data Store flags */
+	flags = DB_CREATE |
+		DB_INIT_CDB |
+		DB_INIT_MPOOL |
+		DB_THREAD;
+	
+	/* Transaction Data Store flags ; not supported yet */
+	/*
+	flags = DB_CREATE |
+		DB_RECOVER |
+		DB_INIT_LOG | 
+		DB_INIT_LOCK |
+		DB_INIT_MPOOL |
+		DB_THREAD |
+		DB_INIT_TXN;
+	*/
+	
+	/* Open the environment */ 
+	if ((rc = env->open(env, _home, flags, 0)) != 0) 
+	{ 
+		ERR("dbenv is not initialized! bdb error: %s.\n",db_strerror(rc));
+		env->err(env, rc, "environment open: %s", _home); 
+		goto err; 
+	}
+	
+	*_dbenv = env;
+	return (0);
+
+err: (void)env->close(env, 0);
+	return (rc);
+}
+
+
+/**
+ */
+bdb_db_p bdblib_get_db(str *dirpath)
+{
+	int rc;
+	bdb_db_p _db_p=NULL;
+
+	if(dirpath==0 || dirpath->s==NULL || dirpath->s[0]=='\0')
+		return NULL;
+
+	if(_bdb_parms==NULL)
+	{
+		ERR("bdb: cache is not initialized! Check if you loaded bdb "
+			"before any other module that uses it.\n");
+		return NULL;
+	}
+
+	if(!bdb_is_database(dirpath->s))
+	{	
+		ERR("bdb: database [%.*s] does not exists!\n",
+				dirpath->len , dirpath->s);
+		return NULL;
+	}
+
+	_db_p = (bdb_db_p)pkg_malloc(sizeof(bdb_db_t));
+	if(!_db_p)
+	{
+		ERR("no private memory for dbenv_t.\n");
+		pkg_free(_db_p);
+		return NULL;
+	}
+
+	_db_p->name.s = (char*)pkg_malloc(dirpath->len*sizeof(char));
+	memcpy(_db_p->name.s, dirpath->s, dirpath->len);
+	_db_p->name.len = dirpath->len;
+
+	if ((rc = bdblib_create_dbenv(&(_db_p->dbenv), dirpath->s)) != 0)
+	{
+		ERR("bdblib_create_dbenv failed");
+		pkg_free(_db_p->name.s);
+		pkg_free(_db_p);
+		return NULL;
+	}
+
+	_db_p->tables=NULL;
+
+	return _db_p;
+}
+
+
+/**
+ * look thru a linked list for the table. if dne, create a new one
+ * and add to the list
+*/
+bdb_tcache_p bdblib_get_table(bdb_db_t *_db, str *_s)
+{
+	bdb_tcache_p _tbc = NULL;
+	bdb_table_p _tp = NULL;
+
+	if(!_db || !_s || !_s->s || _s->len<=0)
+		return NULL;
+
+	if(!_db->dbenv)
+	{
+		return NULL;
+	}
+
+	_tbc = _db->tables;
+	while(_tbc)
+	{
+		if(_tbc->dtp)
+		{
+
+			if(_tbc->dtp->name.len == _s->len 
+				&& !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len ))
+			{
+				return _tbc;
+			}
+		}
+		_tbc = _tbc->next;
+	}
+
+	_tbc = (bdb_tcache_p)pkg_malloc(sizeof(bdb_tcache_t));
+	if(!_tbc)
+		return NULL;
+
+	_tp = bdblib_create_table(_db, _s);
+
+	if(!_tp)
+	{
+		ERR("failed to create table.\n");
+		pkg_free(_tbc);
+		return NULL;
+	}
+
+	_tbc->dtp = _tp;
+
+	if(_db->tables)
+		(_db->tables)->prev = _tbc;
+	
+	_tbc->next = _db->tables;
+	_db->tables = _tbc;
+
+	return _tbc;
+}
+
+
+void bdblib_log(int op, bdb_db_p _db_p, bdb_table_p _tp, char* _msg, int len)
+{
+	if(!_tp || !len) 		return;
+	if(! _bdb_parms->log_enable) 	return;
+	if (_tp->logflags == JLOG_NONE)	return;
+	
+	if ((_tp->logflags & op) == op)
+	{	int op_len=7;
+		char buf[MAX_ROW_SIZE + op_len];
+		char *c;
+		time_t now = time(NULL);
+		
+		if( _bdb_parms->journal_roll_interval)
+		{
+			if((_tp->t) && (now - _tp->t) > _bdb_parms->journal_roll_interval)
+			{	/*try to roll logfile*/
+				if(bdblib_create_journal(_db_p, _tp))
+				{
+					ERR("Journaling has FAILED !\n");
+					return;
+				}
+			}
+		}
+		
+		c = buf;
+		switch (op)
+		{
+		case JLOG_INSERT:
+			strncpy(c, "INSERT|", op_len);
+			break;
+		case JLOG_UPDATE:
+			strncpy(c, "UPDATE|", op_len);
+			break;
+		case JLOG_DELETE:
+			strncpy(c, "DELETE|", op_len);
+			break;
+		}
+		
+		c += op_len;
+		strncpy(c, _msg, len);
+		c +=len;
+		*c = '\n';
+		c++;
+		*c = '\0';
+		
+		if ((_tp->logflags & JLOG_STDOUT) == JLOG_STDOUT)
+			puts(buf);
+		
+		if ((_tp->logflags & JLOG_SYSLOG) == JLOG_SYSLOG)
+			syslog(LOG_LOCAL6, buf);
+		
+		if(_tp->fp) 
+		{
+			if(!fputs(buf, _tp->fp) )
+				fflush(_tp->fp);
+		}
+	}
+}
+
+/**
+ * The function is called to create a handle to a db table.
+ * 
+ * On startup, we do not create any of the db handles.
+ * Instead it is done on first-use (lazy-initialized) to only create handles to 
+ * files (db) that we require.
+ * 
+ * There is one db file per openser table (eg. acc), and they should exist
+ * in your DB_PATH (refer to kamctlrc) directory.
+ *
+ * This function does _not_ create the underlying binary db tables.
+ * Creating the tables MUST be manually performed before 
+ * openser startup by 'kamdbctl create'
+ *
+ * Function returns NULL on error, which will cause openser to exit.
+ *
+ */
+bdb_table_p bdblib_create_table(bdb_db_p _db, str *_s)
+{
+
+	int rc,i,flags;
+	DB *bdb = NULL;
+	bdb_table_p tp = NULL;
+	char tblname[MAX_TABLENAME_SIZE]; 
+
+	if(!_db || !_db->dbenv)
+	{
+		ERR("no bdb_db_p or dbenv.\n");
+		return NULL;
+	}
+
+	tp = (bdb_table_p)pkg_malloc(sizeof(bdb_table_t));
+	if(!tp)
+	{
+		ERR("no private memory for bdb_table_t.\n");
+		return NULL;
+	}
+
+	if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0)
+	{ 
+		_db->dbenv->err(_db->dbenv, rc, "database create");
+		ERR("error in db_create, bdb error: %s.\n",db_strerror(rc));
+		pkg_free(tp);
+		return NULL;
+	}
+
+	memset(tblname, 0, MAX_TABLENAME_SIZE);
+	strncpy(tblname, _s->s, _s->len);
+
+	flags = DB_THREAD;
+
+	if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0)
+	{ 
+		_db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
+		ERR("bdb open failed: %s.\n",db_strerror(rc));
+		pkg_free(tp);
+		return NULL;
+	}
+
+	tp->name.s = (char*)pkg_malloc(_s->len*sizeof(char));
+	memcpy(tp->name.s, _s->s, _s->len);
+	tp->name.len = _s->len;
+	tp->db=bdb;
+	tp->ncols=0;
+	tp->nkeys=0;
+	tp->ro=0;    /*0=ReadWrite ; 1=ReadOnly*/
+	tp->ino=0;   /*inode*/
+	tp->logflags=0; /*bitmap; 4=Delete, 2=Update, 1=Insert, 0=None*/
+	tp->fp=0;
+	tp->t=0;
+	
+	for(i=0;i<MAX_NUM_COLS;i++)
+		tp->colp[i] = NULL;
+
+	/*load metadata; seeded\db_loaded when database are created*/
+	
+	/*initialize columns with metadata*/
+	rc = load_metadata_columns(tp);
+	if(rc!=0)
+	{
+		ERR("FAILED to load METADATA COLS in table: %s.\n", tblname);
+		goto error;
+	}
+	
+	/*initialize columns default values from metadata*/
+	rc = load_metadata_defaults(tp);
+	if(rc!=0)
+	{
+		ERR("FAILED to load METADATA DEFAULTS in table: %s.\n", tblname);
+		goto error;
+	}
+	
+	rc = load_metadata_keys(tp);
+	if(rc!=0)
+	{
+		ERR("FAILED to load METADATA KEYS in table: %s.\n", tblname);
+		/*will have problems later figuring column types*/
+		goto error;
+	}
+
+	/*opened RW by default; Query to set the RO flag */
+	rc = load_metadata_readonly(tp);
+	if(rc!=0)
+	{
+		INFO("No METADATA_READONLY in table: %s.\n", tblname);
+		/*non-critical; table will default to READWRITE*/
+	}
+
+	if(tp->ro)
+	{	
+		/*schema defines this table RO readonly*/
+		
+		if ((rc = bdb->close(bdb,0)) != 0)
+		{ 
+			_db->dbenv->err(_db->dbenv, rc, "DB->close: %s", tblname);
+			ERR("bdb close: %s.\n",db_strerror(rc));
+			goto error;
+		}
+		
+		bdb = NULL;
+		if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0)
+		{ 
+			_db->dbenv->err(_db->dbenv, rc, "database create");
+			ERR("error in db_create.\n");
+			goto error;
+		}
+		
+		flags = DB_THREAD | DB_RDONLY;
+		if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0)
+		{ 
+			_db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
+			ERR("bdb open: %s.\n",db_strerror(rc));
+			goto error;
+		}
+		tp->db=bdb;
+	}
+	
+	/* set the journaling flags; flags indicate which operations
+	   need to be journalled. (e.g possible to only journal INSERT.)
+	*/
+	rc = load_metadata_logflags(tp);
+	if(rc!=0)
+		INFO("No METADATA_LOGFLAGS in table: %s.\n", tblname);
+	
+	if ((tp->logflags & JLOG_FILE) == JLOG_FILE)
+		bdblib_create_journal(_db, tp);
+	
+	return tp;
+	
+error:
+	if(tp) 
+	{
+		pkg_free(tp->name.s);
+		pkg_free(tp);
+	}
+	return NULL;
+}
+
+int bdblib_create_journal(bdb_db_p _db_p, bdb_table_p _tp)
+{
+	char *s;
+	char fn[1024];
+	char d[64];
+	FILE *fp = NULL;
+	struct tm *t;
+	int bl;
+	time_t tim = time(NULL);
+	
+	if(! _db_p || ! _tp) return -1;
+	if(! _bdb_parms->log_enable) return 0;
+	/* journal filename ; e.g. '/var/kamailio/db/location-YYYYMMDDhhmmss.jnl' */
+	s=fn;
+	strncpy(s, _db_p->name.s, _db_p->name.len);
+	s+=_db_p->name.len;
+	
+	*s = '/';
+	s++;
+	
+	strncpy(s, _tp->name.s, _tp->name.len);
+	s+=_tp->name.len;
+	
+	t = localtime( &tim );
+	bl=strftime(d,128,"-%Y%m%d%H%M%S.jnl",t);
+	strncpy(s, d, bl);
+	s+= bl;
+	*s = 0;
+	
+	if(_tp->fp)
+	{	/* must be rolling. */
+		if( fclose(_tp->fp) )
+		{	ERR("Failed to Close Log in table: %.*s .\n", _tp->name.len,
+			 _tp->name.s);
+			return -1;
+		}
+	}
+	
+	if( (fp = fopen(fn, "w")) != NULL )
+	{
+		_tp->fp = fp;
+	}
+	else
+	{
+		ERR("Failed to Open Log in table: %.*s .\n",_tp->name.len, _tp->name.s);
+		return -1;
+	}
+	
+	_tp->t = tim;
+	return 0;
+
+}
+
+int load_metadata_columns(bdb_table_p _tp)
+{
+	int ret,n,len;
+	char dbuf[MAX_ROW_SIZE];
+	char *s = NULL;
+	char cn[64], ct[16];
+	DB *db = NULL;
+	DBT key, data;
+	bdb_col_p col;
+	ret = n = len = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	if(_tp->ncols!=0)
+		return 0;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+
+	key.data = METADATA_COLUMNS;
+	key.size = strlen(METADATA_COLUMNS);
+
+	/*memory for the result*/
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{
+		db->err(db, ret, "load_metadata_columns DB->get failed");
+		ERR("FAILED to find METADATA_COLUMNS in DB \n");
+		return -1;
+	}
+
+	/* eg: dbuf = "bdb_table_name(str) bdb_table_version(int)" */
+	s = strtok(dbuf, " ");
+	while(s!=NULL && n<MAX_NUM_COLS) 
+	{
+		/* eg: meta[0]=table_name  meta[1]=str */
+		sscanf(s,"%20[^(](%10[^)])[^\n]", cn, ct);
+		
+		/* create column*/
+		col = (bdb_col_p) pkg_malloc(sizeof(bdb_col_t));
+		if(!col)
+		{	ERR("out of private memory \n");
+			return -1;
+		}
+		
+		/* set name*/
+		len = strlen( cn );
+		col->name.s = (char*)pkg_malloc(len * sizeof(char));
+		memcpy(col->name.s, cn, len );
+		col->name.len = len;
+		
+		/*set column type*/
+		if(strncmp(ct, "str", 3)==0)
+		{	col->type = DB_STR;
+		}
+		else if(strncmp(ct, "int", 3)==0)
+		{	col->type = DB_INT;
+		}
+		else if(strncmp(ct, "double", 6)==0)
+		{	col->type = DB_DOUBLE;
+		}
+		else if(strncmp(ct, "datetime", 8)==0)
+		{	col->type = DB_DATETIME;
+		}
+		else
+		{	col->type = DB_STR;
+		}
+		
+		col->flag = 0;
+		_tp->colp[n] = col;
+		n++;
+		_tp->ncols++;
+		s=strtok(NULL, " ");
+	}
+
+	return 0;
+}
+
+int load_metadata_keys(bdb_table_p _tp)
+{
+	int ret,n,ci;
+	char dbuf[MAX_ROW_SIZE];
+	char *s = NULL;
+	DB *db = NULL;
+	DBT key, data;
+	ret = n = ci = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	key.data = METADATA_KEY;
+	key.size = strlen(METADATA_KEY);
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{
+		db->err(db, ret, "load_metadata_keys DB->get failed");
+		ERR("FAILED to find METADATA in table \n");
+		return ret;
+	}
+	
+	s = strtok(dbuf, " ");
+	while(s!=NULL && n< _tp->ncols) 
+	{	ret = sscanf(s,"%i", &ci);
+		if(ret != 1) return -1;
+		if( _tp->colp[ci] ) 
+		{	_tp->colp[ci]->flag = 1;
+			_tp->nkeys++;
+		}
+		n++;
+		s=strtok(NULL, " ");
+	}
+
+	return 0;
+}
+
+
+int load_metadata_readonly(bdb_table_p _tp)
+{
+	int i, ret;
+	char dbuf[MAX_ROW_SIZE];
+
+	DB *db = NULL;
+	DBT key, data;
+	i = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	key.data = METADATA_READONLY;
+	key.size = strlen(METADATA_READONLY);
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{	return ret;
+	}
+	
+	if( 1 == sscanf(dbuf,"%i", &i) )
+		_tp->ro=(i>0)?1:0;
+	
+	return 0;
+}
+
+int load_metadata_logflags(bdb_table_p _tp)
+{
+	int i, ret;
+	char dbuf[MAX_ROW_SIZE];
+
+	DB *db = NULL;
+	DBT key, data;
+	i = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	key.data = METADATA_LOGFLAGS;
+	key.size = strlen(METADATA_LOGFLAGS);
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{	return ret;
+	}
+	
+	if( 1 == sscanf(dbuf,"%i", &i) )
+		_tp->logflags=i;
+	
+	return 0;
+}
+
+int load_metadata_defaults(bdb_table_p _tp)
+{
+	int ret,n,len;
+	char dbuf[MAX_ROW_SIZE];
+	char *s = NULL;
+	char cv[64];
+	DB *db = NULL;
+	DBT key, data;
+	bdb_col_p col;
+	ret = n = len = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+
+	key.data = METADATA_DEFAULTS;
+	key.size = strlen(METADATA_DEFAULTS);
+
+	/*memory for the result*/
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{
+		/*no defaults in DB; make some up.*/
+		for(n=0; n<_tp->ncols; n++)
+		{
+			col = _tp->colp[n];
+			if( col ) 
+			{	/*set all columns default value to 'NULL' */
+				len = strlen("NULL");
+				col->dv.s = (char*)pkg_malloc(len * sizeof(char));
+				memcpy(col->dv.s, "NULL", len);
+				col->dv.len = len;
+			}
+		}
+		return 0;
+	}
+	
+	/* use the defaults provided*/
+	s = strtok(dbuf, DELIM);
+	while(s!=NULL && n< _tp->ncols) 
+	{	ret = sscanf(s,"%s", cv);
+		if(ret != 1) return -1;
+		col = _tp->colp[n];
+		if( col ) 
+		{	/*set column default*/
+			len = strlen(s);
+			col->dv.s = (char*)pkg_malloc(len * sizeof(char));
+			memcpy(col->dv.s, cv, len);
+			col->dv.len = len;
+		}
+		n++;
+		s=strtok(NULL, DELIM);
+	}
+	
+	return 0;
+}
+
+inline int bdb_int2str(int _v, char* _s, int* _l)
+{
+	int ret;
+
+	if ((!_s) || (!_l) || (!*_l)) {
+		ERR("Invalid parameter value\n");
+		return -1;
+	}
+
+	ret = snprintf(_s, *_l, "%-d", _v);
+	if (ret < 0 || ret >= *_l) {
+		ERR("Error in snprintf\n");
+		return -1;
+	}
+	*_l = ret;
+
+	return 0;
+}
+
+inline int bdb_double2str(double _v, char* _s, int* _l)
+{
+	int ret;
+
+	if ((!_s) || (!_l) || (!*_l)) {
+		ERR("Invalid parameter value\n");
+		return -1;
+	}
+
+	ret = snprintf(_s, *_l, "%-10.2f", _v);
+	if (ret < 0 || ret >= *_l) {
+		ERR("Error in snprintf\n");
+		return -1;
+	}
+	*_l = ret;
+
+	return 0;
+}
+
+inline int bdb_time2str(time_t _v, char* _s, int* _l)
+{
+	struct tm* t;
+	int l;
+
+	if ((!_s) || (!_l) || (*_l < 2)) {
+		ERR("Invalid parameter value\n");
+		return -1;
+	}
+
+	*_s++ = '\'';
+
+	/* Convert time_t structure to format accepted by the database */
+	t = localtime(&_v);
+	l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", t);
+
+	if (l == 0) {
+		ERR("Error during time conversion\n");
+		/* the value of _s is now unspecified */
+		_s = NULL;
+		_l = 0;
+		return -1;
+	}
+	*_l = l;
+
+	*(_s + l) = '\'';
+	*_l = l + 2;
+	return 0;
+}
+
+/*
+ * Used when converting result from a query
+ */
+int bdb_val2str(db_fld_t *fld, char *sout, int *slen)
+{
+	int l;
+	db_fld_val_t *val;
+
+	if (fld->flags&DB_NULL) 
+	{
+		*slen = snprintf(sout, *slen, "NULL");
+		return 0;
+	}
+	
+	val = &(fld->v);
+	switch(fld->type)
+	{
+		case DB_INT:
+			if (bdb_int2str(val->int4, sout, slen) < 0) {
+				ERR("Error while converting int to string\n");
+				return -2;
+			} else {
+				DBG("Converted int to string\n");
+				return 0;
+			}
+		break;
+
+		case DB_BITMAP:
+			if (bdb_int2str(val->bitmap, sout, slen) < 0) {
+				ERR("Error while converting bitmap to string\n");
+				return -3;
+			} else {
+				DBG("Converted bitmap to string\n");
+				return 0;
+			}
+		break;
+
+		case DB_DOUBLE:
+			if (bdb_double2str(val->dbl, sout, slen) < 0) {
+				ERR("Error while converting double  to string\n");
+				return -3;
+			} else {
+				DBG("Converted double to string\n");
+				return 0;
+			}
+		break;
+
+		case DB_CSTR:
+			l = strlen(val->cstr);
+			if (*slen < l ) 
+			{
+				ERR("Destination buffer too short for string\n");
+				return -4;
+			} else {
+				DBG("Converted string to string\n");
+				strncpy(sout, val->cstr , l);
+				sout[l] = 0;
+				*slen = l;
+				return 0;
+			}
+		break;
+
+		case DB_STR:
+			l = val->lstr.len;
+			if (*slen < l) 
+			{
+				ERR("Destination buffer too short for str\n");
+				return -5;
+			} else {
+				DBG("Converted str to string\n");
+				strncpy(sout, val->lstr.s , val->lstr.len);
+				*slen = val->lstr.len;
+				return 0;
+			}
+		break;
+
+		case DB_DATETIME:
+			if (bdb_time2str(val->time, sout, slen) < 0) {
+				ERR("Error while converting time_t to string\n");
+				return -6;
+			} else {
+				DBG("Converted time_t to string\n");
+				return 0;
+			}
+		break;
+
+		case DB_BLOB:
+			l = val->blob.len;
+			if (*slen < l) 
+			{
+				ERR("Destination buffer too short for blob\n");
+				return -7;
+			} else {
+				DBG("Converting BLOB [%s]\n", sout);
+				memcpy(sout, val->blob.s , val->blob.len);
+				*slen = l;
+				return 0;
+			}
+		break;
+
+		default:
+			DBG("Unknown data type\n");
+			return -8;
+	}
+}
+
+/*creates a composite key _k of length _klen from n values of _v;
+  provide your own initialized memory for target _k and _klen;
+  resulting value: _k = "KEY1 | KEY2"
+  ko = key only
+*/
+
+int bdblib_valtochar(bdb_table_p tp, db_fld_t *fld, int fld_count, char *kout,
+		int *klen, int ktype)
+{
+	char *p; 
+	static char sk[MAX_ROW_SIZE]; // subkey(sk) val
+	char* delim = DELIM;
+	char* cNULL = "NULL";
+	int  len, total, sum;
+	int i, j, k;
+	bdb_fld_t *f;
+
+	p =  kout;
+	len = sum = total = 0;
+	i = j = k = 0;
+	
+	if(tp==NULL) return -1;
+	if(fld==NULL || fld_count<1) return -1;
+	if(kout==NULL || klen==NULL ) return -1;
+	if( *klen < 1)    return -1;
+	
+	memset(sk, 0, MAX_ROW_SIZE);
+	total = *klen;
+	*klen = 0; //sum
+	
+	/*
+	  schema has specified keys
+	  verify all schema keys are provided
+	  use 'NULL' for those that are missing.
+	*/
+	for(i=0; i<tp->ncols; i++)
+	{	/* i indexes columns in schema order */
+		if(ktype)
+		{	/* keymode; skip over non-key columns */
+			if(tp->colp[i]->flag==0) 
+				continue; 
+		}
+		
+		for(j=0; j<fld_count; j++)
+		{
+			f = DB_GET_PAYLOAD(fld + j);
+			/*
+			  j indexes the columns provided in _k
+			  which may be less than the total required by
+			  the schema. the app does not know the order
+			  of the columns in our schema!
+			 */
+			k = f->col_pos;
+			
+			/*
+			 * k index will remap back to our schema order; like i
+			 */
+			if(i == k)
+			{
+				/*
+				 KEY was provided; append to buffer;
+				 _k[j] contains a key, but its a key that 
+				 corresponds to column k of our schema.
+				 now we know its a match, and we dont need
+				 index k for anything else
+				*/
+				len = total - sum;
+				if ( bdb_val2str((fld+j), sk, &len) != 0)
+				{
+					ERR("Destination buffer too short for subval %s\n",sk);
+					return -4;
+				}
+				
+				sum += len;
+				if(sum > total)
+				{
+					ERR("Destination buffer too short for subval %s\n",sk);
+					return -5;
+				}
+
+				strncpy(p, sk, len);
+				p += len;
+				*klen = sum;
+
+				sum += DELIM_LEN;
+				if(sum > total)
+				{
+					ERR("Destination buffer too short for delim \n");
+					return -5;
+				} 
+				
+				/* append delim */
+				strncpy(p, delim, DELIM_LEN);
+				p += DELIM_LEN;
+				*klen = sum;
+				
+				
+				/* take us out of inner for loop
+				   and at the end of the outer loop
+				   to look for our next schema key
+				*/
+				goto next;
+			}
+		}
+
+		/*
+		 NO KEY provided; use the column default value (dv)
+		     i.e _tp->colp[i]->dv
+		*/
+		len = tp->colp[i]->dv.len;
+		sum += len;
+		if(sum > total)
+		{
+			ERR("Destination buffer too short for subval %s\n",cNULL);
+			return -5;
+		}
+		
+		strncpy(p, tp->colp[i]->dv.s, len);
+		p += len;
+		*klen = sum;
+		
+		sum += DELIM_LEN;
+		if(sum > total)
+		{
+			ERR("Destination buffer too short for delim \n");
+			return -5;
+		} 
+		
+		strncpy(p, delim, DELIM_LEN);
+		p += DELIM_LEN;
+		*klen = sum;
+next:
+		continue;
+	}
+
+	return 0;
+}
+
+
+/**
+ *
+ */
+int bdb_db_free(bdb_db_p _dbp)
+{
+	bdb_tcache_p _tbc = NULL, _tbc0=NULL;
+	if(!_dbp)
+		return -1;
+
+	_tbc = _dbp->tables;
+
+	while(_tbc)
+	{
+		_tbc0 = _tbc->next;
+		bdb_tcache_free(_tbc);
+		_tbc = _tbc0;
+	}
+	
+	if(_dbp->dbenv)
+		_dbp->dbenv->close(_dbp->dbenv, 0);
+	
+	if(_dbp->name.s)
+		pkg_free(_dbp->name.s);
+	
+	pkg_free(_dbp);
+
+	return 0;
+}
+
+
+/**
+ *
+ */
+int bdb_tcache_free(bdb_tcache_p _tbc)
+{
+	if(!_tbc)
+		return -1;
+	
+	/*while ??!? */	
+	if(_tbc->dtp)
+		bdb_table_free(_tbc->dtp);
+	
+	pkg_free(_tbc);
+
+	return 0;
+}
+
+
+/**
+ * close DB (sync data to disk) and free mem
+ */
+int bdb_table_free(bdb_table_p _tp)
+{	int i;
+	if(!_tp)
+		return -1;
+
+	if(_tp->db)
+		_tp->db->close(_tp->db, 0);
+	
+	if(_tp->fp)
+		fclose(_tp->fp);
+
+	if(_tp->name.s)
+		pkg_free(_tp->name.s);
+	
+	for(i=0;i<_tp->ncols;i++)
+	{	if(_tp->colp[i])
+		{	pkg_free(_tp->colp[i]->name.s);
+			pkg_free(_tp->colp[i]->dv.s);
+			pkg_free(_tp->colp[i]);
+		}
+	}
+
+	pkg_free(_tp);
+	return 0;
+}
+
+int bdblib_recover(bdb_table_p _tp, int _rc)
+{
+	switch(_rc)
+	{
+		case DB_LOCK_DEADLOCK:
+		ERR("DB_LOCK_DEADLOCK detected !!\n");
+		
+		case DB_RUNRECOVERY:
+		ERR("DB_RUNRECOVERY detected !! \n");
+		bdblib_destroy();
+		exit(1);
+		break;
+	}
+	
+	return 0;
+}
+
+/**
+ *
+ */
+int bdb_is_database(char *dirpath)
+{
+	DIR *dirp = NULL;
+	
+	if(dirpath==NULL || dirpath[0]=='\0')
+		return 0;
+	dirp = opendir(dirpath);
+	if(dirp==NULL)
+		return 0;
+	closedir(dirp);
+
+	return 1;
+}
+
+int bdb_get_colpos(bdb_table_t *tp, char *name)
+{
+	str s;
+	int i;
+
+	if(tp==NULL || name==NULL)
+	{
+		ERR("bdb: bad parameters\n");
+		return -1;
+	}
+
+	s.s = name;
+	s.len = strlen(name);
+	for(i=0; i<tp->ncols; i++) {
+		if(tp->colp[i]->name.len == s.len
+				&& !strncasecmp(s.s, tp->colp[i]->name.s, s.len))
+			return i;
+	}
+	return -1;
+}
+
+int bdb_str2time(char *s, time_t *v)
+{
+	struct tm time;
+
+	if ((!s) || (!v)) {
+		ERR("bdb:invalid parameter value\n");
+		return -1;
+	}
+
+	memset(&time, '\0', sizeof(struct tm));
+	//if (strptime(s, "%Y-%m-%d %H:%M:%S", &time) == NULL) {
+	//	ERR("Error during time conversion\n");
+	//	return -1;
+	//}
+
+	time.tm_isdst = -1;
+	*v = mktime(&time);
+
+	return 0;
+}
+
+int bdb_str2double(char *s, double *v)
+{
+	if ((!s) || (!v)) {
+		ERR("Invalid parameter value\n");
+		return -1;
+	}
+
+	*v = atof(s);
+	return 0;
+}
+
+int bdb_str2int(char *s, int *v)
+{
+	long tmp;
+
+	if (!s || !v) {
+		ERR("Invalid parameter value\n");
+		return -1;
+	}
+
+	tmp = strtoul(s, 0, 10);
+	if ((tmp == ULONG_MAX && errno == ERANGE) || 
+	    (tmp < INT_MIN) || (tmp > UINT_MAX)) {
+		ERR("Value out of range\n");
+		return -1;
+	}
+
+	*v = (int)tmp;
+	return 0;
+}

+ 179 - 0
modules/db_berkeley/bdb_lib.h

@@ -0,0 +1,179 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#ifndef _BDB_LIB_H_
+#define _BDB_LIB_H_
+
+#include <time.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "../../str.h"
+#include "../../lib/srdb2/db.h"
+#include "../../lib/srdb2/db_fld.h"
+
+/*max number of columns in a table*/
+#define MAX_NUM_COLS 32
+
+/*max char width of a table row*/
+#define MAX_ROW_SIZE 2048
+
+/*max char width of a table name*/
+#define MAX_TABLENAME_SIZE 64
+
+#define METADATA_COLUMNS "METADATA_COLUMNS"
+#define METADATA_KEY "METADATA_KEY"
+#define METADATA_READONLY "METADATA_READONLY"
+#define METADATA_LOGFLAGS "METADATA_LOGFLAGS"
+#define METADATA_DEFAULTS "METADATA_DEFAULTS"
+
+/*journal logging flag masks */
+#define JLOG_NONE   0
+#define JLOG_INSERT 1
+#define JLOG_DELETE 2
+#define JLOG_UPDATE 4
+#define JLOG_FILE   8
+#define JLOG_STDOUT 16
+#define JLOG_SYSLOG 32
+
+#define DELIM "|"
+#define DELIM_LEN (sizeof(DELIM)-1)
+
+#define BDB_VALUE 0
+#define BDB_KEY   1
+
+typedef enum db_fld_type bdb_type_t;
+
+typedef struct {
+	bdb_type_t type; /**< Type of the value                              */
+	int nul;		/**< Means that the column in database has no value */
+	int free;		/**< Means that the value should be freed */
+	/** Column value structure that holds the actual data in a union.  */
+	union {
+		int           int_val;    /**< integer value              */
+		long long     ll_val;     /**< long long value            */
+		double        double_val; /**< double value               */
+		time_t        time_val;   /**< unix time_t value          */
+		const char*   string_val; /**< zero terminated string     */
+		str           str_val;    /**< str type string value      */
+		str           blob_val;   /**< binary object data         */
+		unsigned int  bitmap_val; /**< Bitmap data type           */
+	} val;
+} bdb_val_t, *bdb_val_p;
+
+// typedef db_val_t bdb_val_t, *bdb_val_p;
+
+typedef struct _bdb_row
+{
+	bdb_val_p fields;
+	struct _bdb_row *prev;
+	struct _bdb_row *next;
+} bdb_row_t, *bdb_row_p;
+
+typedef struct _bdb_col
+{
+	str name;
+	str dv;     /* default value */
+	int type;
+	int flag;
+} bdb_col_t, *bdb_col_p;
+
+typedef struct _bdb_table
+{
+	str name;
+	DB *db;
+	bdb_col_p colp [MAX_NUM_COLS];
+	int ncols;
+	int nkeys;
+	int ro;       /*db readonly flag*/
+	int logflags; /*flags indication what-where to journal log */
+	FILE* fp;     /*jlog file pointer */
+	time_t t;     /*jlog creation time */
+	ino_t ino;
+} bdb_table_t, *bdb_table_p;
+
+typedef struct _bdb_tcache
+{
+	bdb_table_p dtp;
+	struct _bdb_tcache *prev;
+	struct _bdb_tcache *next;
+} bdb_tcache_t, *bdb_tcache_p;
+
+typedef struct _bdb_db
+{
+	str name;
+	DB_ENV *dbenv;
+	bdb_tcache_p tables;
+} bdb_db_t, *bdb_db_p;
+
+typedef struct _bdb_params
+{
+	u_int32_t cache_size;
+	int auto_reload;
+	int log_enable;
+	int journal_roll_interval;
+} bdb_params_t, *bdb_params_p;
+
+
+int bdblib_init(bdb_params_p _parms);
+int bdblib_destroy(void);
+int bdblib_close(bdb_db_p _db_p, str* _n);
+int bdblib_reopen(bdb_db_p _db_p, str* _n);
+int bdblib_recover(bdb_table_p _tp, int error_code);
+void bdblib_log(int op, bdb_db_p _db, bdb_table_p _tp, char* _msg, int len);
+int bdblib_create_dbenv(DB_ENV **dbenv, char* home);
+int bdblib_create_journal(bdb_db_p _db_p, bdb_table_p _tp);
+bdb_db_p  	bdblib_get_db(str *_s);
+bdb_tcache_p 	bdblib_get_table(bdb_db_t *_db, str *_s);
+bdb_table_p 	bdblib_create_table(bdb_db_t *_db, str *_s);
+
+int bdb_db_free(bdb_db_p _dbp);
+int bdb_tcache_free(bdb_tcache_p _tbc);
+int bdb_table_free(bdb_table_p _tp);
+
+int load_metadata_columns(bdb_table_p _tp);
+int load_metadata_keys(bdb_table_p _tp);
+int load_metadata_readonly(bdb_table_p _tp);
+int load_metadata_logflags(bdb_table_p _tp);
+int load_metadata_defaults(bdb_table_p _tp);
+
+int bdblib_valtochar(bdb_table_p tp, db_fld_t *fld, int fld_count, char *kout,
+		int *klen, int ktype);
+
+int bdb_is_database(char *dirpath);
+int bdb_get_colpos(bdb_table_t *tp, char *name);
+
+int bdb_str2int(char *s, int *v);
+int bdb_str2double(char *s, double *v);
+int bdb_str2time(char *s, time_t *v);
+
+#endif

+ 124 - 0
modules/db_berkeley/bdb_mod.c

@@ -0,0 +1,124 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+#include "../../str.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+
+#include "../../sr_module.h"
+#include "../../lib/srdb2/db_res.h"
+#include "../../lib/srdb2/db.h"
+
+#include "bdb_lib.h"
+#include "bdb_con.h"
+#include "bdb_uri.h"
+#include "bdb_fld.h"
+#include "bdb_res.h"
+#include "bdb_cmd.h"
+#include "km_db_berkeley.h"
+
+MODULE_VERSION
+
+int auto_reload = 0;
+int log_enable  = 0;
+int journal_roll_interval = 0;
+
+static int  bdb_mod_init(void);
+static void bdb_mod_destroy(void);
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"db_ctx",    (cmd_function)NULL,          0, 0, 0},
+	{"db_con",    (cmd_function)bdb_con,       0, 0, 0},
+	{"db_uri",    (cmd_function)bdb_uri,       0, 0, 0},
+	{"db_cmd",    (cmd_function)bdb_cmd,       0, 0, 0},
+	{"db_put",    (cmd_function)bdb_cmd_exec,  0, 0, 0},
+	{"db_del",    (cmd_function)bdb_cmd_exec,  0, 0, 0},
+	{"db_get",    (cmd_function)bdb_cmd_exec,  0, 0, 0},
+	{"db_upd",    (cmd_function)bdb_cmd_exec,  0, 0, 0},
+	{"db_sql",    (cmd_function)bdb_cmd_exec,  0, 0, 0},
+	{"db_first",  (cmd_function)bdb_cmd_first, 0, 0, 0},
+	{"db_next",   (cmd_function)bdb_cmd_next,  0, 0, 0},
+	{"db_res",    (cmd_function)bdb_res,       0, 0, 0},
+	{"db_fld",    (cmd_function)bdb_fld,       0, 0, 0},
+	{"db_bind_api", (cmd_function)bdb_bind_api, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"auto_reload",        INT_PARAM, &auto_reload },
+	{"log_enable",         INT_PARAM, &log_enable  },
+	{"journal_roll_interval", INT_PARAM, &journal_roll_interval  },
+	{0, 0, 0}
+};
+
+struct module_exports exports = {	
+	"bdb",
+	cmds,     /* Exported functions */
+	0,        /* RPC method */
+	params,   /* Exported parameters */
+	bdb_mod_init,     /* module initialization function */
+	0,        /* response function*/
+	bdb_mod_destroy,  /* destroy function */
+	0,        /* oncancel function */
+	0         /* per-child init function */
+};
+
+
+static int bdb_mod_init(void)
+{
+	bdb_params_t p;
+	
+	p.auto_reload = auto_reload;
+	p.log_enable = log_enable;
+	p.cache_size  = (4 * 1024 * 1024); //4Mb
+	p.journal_roll_interval = journal_roll_interval;
+	
+	if(bdblib_init(&p))
+		return -1;
+
+	return km_mod_init();
+}
+
+static void bdb_mod_destroy(void)
+{
+	km_destroy();
+	bdblib_destroy();
+}
+

+ 37 - 0
modules/db_berkeley/bdb_mod.h

@@ -0,0 +1,37 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+#ifndef _BDB_MOD_H
+#define _BDB_MOD_H
+
+extern int auto_reload;
+extern int log_enable;
+extern int journal_roll_interval;
+
+#endif /* _BDB_MOD_H */

+ 81 - 0
modules/db_berkeley/bdb_res.c

@@ -0,0 +1,81 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2006-2007 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <db.h>
+
+#include "bdb_res.h"
+#include "bdb_cmd.h"
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../lib/srdb2/db_gen.h"
+
+
+void bdb_res_free(db_res_t* res, bdb_res_t *payload)
+{
+	bdb_cmd_t *bcmd;
+
+	bcmd = DB_GET_PAYLOAD(res->cmd);
+
+	/* free bdb result */
+
+	if(bcmd->dbcp!=NULL)
+	{
+		bcmd->dbcp->close(bcmd->dbcp);
+		bcmd->dbcp = NULL;
+	}
+	db_drv_free(&payload->gen);
+	pkg_free(payload);
+}
+
+
+/*
+ * Attach bdb specific structure to db_res, this structure contains a pointer
+ * to bdb_res_free which releases the result stored in the oracle statement
+ * and if there is a cursor open in the statement then it will be closed as well
+ */
+int bdb_res(db_res_t* res)
+{
+	bdb_res_t *br;
+
+	br = (bdb_res_t*)pkg_malloc(sizeof(bdb_res_t));
+	if (br == NULL) {
+		ERR("bdb: No memory left\n");
+		return -1;
+	}
+	if (db_drv_init(&br->gen, bdb_res_free) < 0) goto error;
+	DB_SET_PAYLOAD(res, br);
+	return 0;
+	
+error:
+	if (br) {
+		db_drv_free(&br->gen);
+		pkg_free(br);
+	}
+	return -1;
+}

+ 41 - 0
modules/db_berkeley/bdb_res.h

@@ -0,0 +1,41 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2006-2007 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _BDB_RES_H_
+#define _BDB_RES_H_
+
+#include "../../lib/srdb2/db_drv.h"
+#include "../../lib/srdb2/db_res.h"
+
+typedef struct _bdb_res {
+	db_drv_t gen;
+} bdb_res_t;
+
+int bdb_res(db_res_t* cmd);
+
+#endif /* _BDB_RES_H_ */

+ 164 - 0
modules/db_berkeley/bdb_uri.c

@@ -0,0 +1,164 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * The implementation of parser parsing bdb:.. URIs.
+ */
+
+#include <string.h>
+
+#include "../../mem/mem.h"
+#include "../../ut.h"
+
+#include "bdb_uri.h"
+
+#ifndef CFG_DIR
+#define CFG_DIR "/tmp"
+#endif
+
+#define BDB_ID		"bdb://"
+#define BDB_ID_LEN	(sizeof(BDB_ID)-1)
+
+/** compare s1 & s2  with a function f (which should return 0 if ==);
+ * s1 & s2 can be null
+ * return 0 if match, 1 if not
+ */
+#define cmpstr(s1, s2, f) \
+	((s1)!=(s2)) && ((s1)==0 || (s2)==0 || (f)((s1), (s2))!=0)
+
+
+/** Compares two BDB connection URIs.
+ * This function is called whenever the database abstraction layer in
+ * SER needs to compare to URIs with the bdb scheme. The function
+ * compares hosts and port numbers of both URIs (host part comparison
+ * is case insensitive). The URI comparison is mainly used to
+ * by the connection pool to determine if a connection to a given
+ * server already exists.
+ **/
+static unsigned char bdb_uri_cmp(db_uri_t* uri1, db_uri_t* uri2)
+{
+	bdb_uri_t * buri1, *buri2;
+
+	if (!uri1 || !uri2) return 0;
+
+	buri1 = DB_GET_PAYLOAD(uri1);
+	buri2 = DB_GET_PAYLOAD(uri2);
+
+	if (cmpstr(buri1->uri, buri2->uri, strcmp))
+		return 0;
+	return 1;
+}
+
+/*
+ * Parse BDB URI of form
+ * //path/to/dir
+ *
+ * Returns 0 if parsing was successful and -1 otherwise
+ */
+int parse_bdb_uri(bdb_uri_t* res, str* uri)
+{
+	str s;
+
+	if(uri==NULL || uri->s==NULL)
+		return -1;
+
+	s = *uri;
+
+	res->uri = (char*)pkg_malloc((s.len+1)*sizeof(char));
+
+	if(res->uri == NULL)
+	{
+		ERR("bdb: no more pkg\n");
+		return -1;
+	}
+
+	memcpy(res->uri, s.s, s.len);
+	res->uri[s.len] = '\0';
+
+	if(s.s[0]!='/')
+	{
+		res->path.s = (char*)pkg_malloc((sizeof(CFG_DIR)+s.len+2)*sizeof(char));
+		memset(res->path.s, 0, (sizeof(CFG_DIR)+s.len+2)*sizeof(char));
+		if(res->path.s==NULL)
+		{
+			ERR("bdb: no more pkg.\n");
+			pkg_free(res->uri);
+			res->uri = NULL;
+			return -1;
+		}
+		strcpy(res->path.s, CFG_DIR);
+		res->path.s[sizeof(CFG_DIR)] = '/';
+		strncpy(&res->path.s[sizeof(CFG_DIR)+1], s.s, s.len);
+		res->path.len = sizeof(CFG_DIR)+s.len;
+	} else {
+		res->path.s = res->uri;
+		res->path.len = strlen(res->path.s);
+	}
+	
+	return 0;
+}
+
+static void bdb_uri_free(db_uri_t* uri, bdb_uri_t* payload)
+{
+	if (payload == NULL) return;
+	if(payload->path.s && payload->path.s!=payload->uri)
+		pkg_free(payload->path.s);
+	if (payload->uri) pkg_free(payload->uri);
+	db_drv_free(&payload->drv);
+	pkg_free(payload);
+}
+
+
+int bdb_uri(db_uri_t* uri)
+{
+	bdb_uri_t *buri;
+
+	buri = (bdb_uri_t*)pkg_malloc(sizeof(bdb_uri_t));
+	if (buri == NULL) {
+		ERR("bdb: No memory left\n");
+		goto error;
+	}
+	memset(buri, '\0', sizeof(bdb_uri_t));
+	if (db_drv_init(&buri->drv, bdb_uri_free) < 0) goto error;
+    if (parse_bdb_uri(buri,  &uri->body) < 0) goto error;
+
+	DB_SET_PAYLOAD(uri, buri);
+	uri->cmp = bdb_uri_cmp;
+	return 0;
+
+ error:
+	if (buri) {
+		if (buri->uri) pkg_free(buri->uri);
+		db_drv_free(&buri->drv);
+		pkg_free(buri);
+	}
+	return -1;
+}
+
+
+/** @} */

+ 65 - 0
modules/db_berkeley/bdb_uri.h

@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * BDB Database Driver for SER
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _BDB_URI_H_
+#define _BDB_URI_H_
+
+/** \addtogroup bdb
+ * @{
+ */
+
+/** \file
+ * The functions parsing and interpreting bdb: URIs.
+ */
+
+#include "../../lib/srdb2/db_uri.h"
+#include "../../lib/srdb2/db_drv.h"
+
+#include <db.h>
+
+/** LDAP driver specific payload to attach to db_uri structures.
+ * This is the LDAP specific structure that will be attached
+ * to generic db_uri structures in the database API in SER. The
+ * structure contains parsed elements of the ldap: URI.
+ */
+typedef struct bdb_uri {
+	db_drv_t drv;
+	char* uri;             /**< The whole URI, including scheme */
+	str path;
+} bdb_uri_t, *bdb_uri_p;
+
+
+/** Create a new bdb_uri structure and parse the URI in parameter.
+ * This function builds a new bdb_uri structure from the body of
+ * the generic URI given to it in parameter.
+ * @param uri A generic db_uri structure.
+ * @retval 0 on success
+ * @retval A negative number on error.
+ */
+int bdb_uri(db_uri_t* uri);
+
+
+/** @} */
+
+#endif /* _BDB_URI_H_ */

+ 55 - 0
modules/db_berkeley/doc/db_berkeley.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+
+<!ENTITY admin SYSTEM "db_berkeley_admin.xml">
+<!ENTITY faq SYSTEM "../../../doc/module_faq.xml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.xml">
+%docentities;
+
+]>
+
+<book>
+    <bookinfo>
+	<title>Berkeley DB Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Will</firstname>
+		<surname>Quan</surname>
+		<affiliation><orgname>Cisco Systems</orgname></affiliation>
+		<address>
+		<email>[email protected]</email>
+		<otheraddr>
+		<ulink url="http://www.cisco.com">http://www.cisco.com</ulink>
+		</otheraddr>
+		</address>
+	    </author>
+	    <editor>
+		<firstname>Will</firstname>
+		<surname>Quan</surname>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2007</year>
+	    <holder>Cisco Systems</holder>
+	</copyright>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision: 846 $</revnumber>
+		<date>$Date: 2006-05-22 09:15:40 -0500 (Mon, 22 May 2006) $</date>
+	    </revision>
+	</revhistory>
+    </bookinfo>
+    <toc></toc>
+    
+    &admin;
+    &faq;
+    
+</book>

+ 582 - 0
modules/db_berkeley/doc/db_berkeley_admin.xml

@@ -0,0 +1,582 @@
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This is a module which integrates the Berkeley DB into Kamailio.
+		It implements the DB API defined in Kamailio.
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>No dependencies on other &kamailio; modules</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>Berkeley Berkeley DB 4.6</emphasis> - an embedded database.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>auto_reload</varname> (integer)</title>
+		<para>
+		The auto-reload will close and reopen a Berkeley DB when the
+		files inode has changed. The operation occurs only duing a query. 
+		Other operations such as insert or delete, do not invoke auto_reload.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (1 - on / 0 - off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>auto_reload</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_berkeley", "auto_reload", 1)
+...
+		</programlisting>
+		</example>
+	</section>
+	
+	<section>
+		<title><varname>log_enable</varname> (integer)</title>
+		<para>
+		The log_enable boolean controls when to create journal files.
+		The following operations can be journaled: 
+		INSERT, UPDATE, DELETE. Other operations such as SELECT, do not. 
+		This journaling are required if you need to recover from a corrupt 
+		DB file. That is, kambdb_recover requires these to rebuild 
+		the db file. If you find this log feature useful, you may 
+		also be interested in the METADATA_LOGFLAGS bitfield that each 
+		table has. It will allow you to control which operations to 
+		journal, and the destination (like syslog, stdout, local-file). 
+		Refer to bdblib_log()  and documentation on METADATA.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (1 - on / 0 - off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>log_enable</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_berkeley", "log_enable", 1)
+...
+		</programlisting>
+		</example>
+	</section>
+	
+	<section>
+		<title><varname>journal_roll_interval</varname> (integer seconds)</title>
+		<para>
+		The journal_roll_interval will close and open a new log file. 
+		The roll operation occurs only at the end of writing a log, 
+		so it is not guaranteed to to roll 'on time'.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>journal_roll_interval</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_berkeley", "journal_roll_interval", 3600)
+...
+		</programlisting>
+		</example>
+	</section>
+	</section>
+	
+	<section>
+	<title>Exported Functions</title>
+		<para>
+		No function exported to be used from configuration file.
+		</para>
+	</section>
+	
+	<section>
+	<title>Exported MI Functions</title>
+	<section>
+		<title><function moreinfo="none">bdb_reload</function></title>
+		
+		<para>
+		Causes db_berkeley module to re-read the contents of specified table (or dbenv).
+		The db_berkeley DB actually loads each table on demand, as opposed to loading all
+		at mod_init time. The bdb_reload operation is implemented as a close followed by a reopen.
+		Note- bdb_reload will fail if a table has not been accessed before (because the close 
+		will fail).
+		</para>
+		
+		<para>
+		Name: <emphasis>bdb_reload</emphasis>
+		</para>
+		
+		<para>Parameters: <emphasis>tablename (or db_path); to reload a particular table 
+		provide the tablename as the arguement (eg subscriber); to reload all tables provide the db_path to
+		the db files. The path can be found in kamctlrc DB_PATH variable. 
+		</emphasis></para>
+	</section>
+	</section>
+	
+	<section>
+	<title>Installation and Running</title>
+		<para>
+		First download, compile and install the Berkeley DB. This is 
+		outside the scope of this document. Documentation for this 
+		procedure is available on the Internet.
+		</para>
+		
+		<para>
+		Next, prepare to compile Kamailio with the db_berkeley module. 
+		In the directory /modules/db_berkeley, modify the Makefile to point 
+		to your distribution of Berkeley DB. You may also define 'BDB_EXTRA_DEBUG' 
+		to compile in extra debug logs. However, it is not a recommended 
+		deployment to production servers.
+		</para>
+		
+		<para>
+		Because the module dependes on an external library, the db_berkeley module is not
+		compiled and installed by default. You can use one of the next options.
+		</para>
+		
+		<itemizedlist>
+			<listitem>
+			<para>
+			edit the "Makefile" and remove "db_berkeley" from "excluded_modules"
+			list. Then follow the standard procedure to install &kamailio;:
+			"make all; make install".
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			from command line use: 'make all include_modules="db_berkeley";
+			make install include_modules="db_berkeley"'.
+			</para>
+			</listitem>
+		</itemizedlist>
+		
+		<para>
+		Installation of Kamailio is performed by simply running make install
+		as root user of the main directory. This will install the binaries 
+		in /usr/local/sbin/.
+		If this was successful, openser control engine files should now 
+		be installed as /usr/local/sbin/kamdbctl.
+		</para>
+		
+		<para>
+		Decide where (on the filesystem) you want to install the Berkeley DB files.
+		For instance, '/usr/local/etc/kamailio/db_berkeley' directory.
+		Make note of this directory as we need to add this path to the kamctlrc file.
+		Note: Kamailio will not startup without these DB files.
+		</para>
+		
+		<para>
+		Edit kamctlrc - There are two parameters in this file that should be 
+		configured before openserctrdb script can work properly: DBENGINE and DB_PATH.
+		Edit file: '/usr/local/etc/kamailio/kamctlrc'
+		</para>
+	        <programlisting  format="linespecific">
+		## database type: MYSQL, PGSQL, DB_BERKELEY, or DBTEXT, by default none is loaded
+		# DBENGINE=DB_BERKELEY
+		
+		## database path used by dbtext or db_berkeley
+		# DB_PATH="/usr/local/etc/kamailio/db_berkeley"
+		</programlisting>
+		
+		<para>
+		(Optional) Pre creation step- Customize your meta-data.
+		The DB files are initially seeded with necessary meta-data. 
+		This is a good time to review the meta-data section details,
+		before making modifications to your tables dbschema.
+		By default, the files are installed in '/usr/local/share/kamailio/db_berkeley/openser'
+		By default these tables are created Read/Write and without any journalling as 
+		shown. These settings can be modified on a per table basis.
+		Note: If you plan to use kambdb_recover, you must change the LOGFLAGS.
+		</para>
+	        <programlisting  format="linespecific">
+		METADATA_READONLY
+		0
+		METADATA_LOGFLAGS
+		0
+		</programlisting>
+		
+		
+		<para>
+		Execute kamdbctl - There are three (3) groups of tables you may need depending
+		on your situation.
+		</para>
+	        <programlisting  format="linespecific">
+		kamdbctl create   		(required)
+		kamdbctl presence 		(optional)
+		kamdbctl extra    		(optional)
+		</programlisting>
+		
+		<para>
+		Modify the Kamailio configuration file to use db_berkeley module. 
+		The database URL for modules must be the path to the directory where 
+		the Berkeley DB table-files are located, prefixed by "berkeley://", 
+		e.g., "berkeley:///usr/local/etc/kamailio/db_berkeley". 
+		</para>
+		
+		<para>
+		A couple other IMPORTANT things to consider are the 'db_mode' and the 'use_domain' 
+		modparams. The description of these parameters are found in usrloc documentation.
+		</para>
+		
+		<para>
+		Note on db_mode- 
+		The db_berkeley module will only journal the moment usrloc writes back
+		to the DB. The safest mode is mode 3 , since the db_berkeley journal files will always
+		be up-to-date. The main point is the db_mode vs. recovery by journal file interaction.
+		
+		Writing journal entries is 'best effort'. So if the hard drive becomes full, the
+		attempt to write a journal entry may fail.
+		</para>
+		
+		<para>
+		Note on use_domain-
+		The db_berkeley module will attempt natural joins when performing a query.
+		This is basically a lexigraphical string compare using the keys provided.
+		In most places in the db_berkeley dbschema (unless you customize), the domainname 
+		is identified as a natural key. 
+		Consider an example where use_domain = 0. In table subscriber, the db will be keying on 
+		'username|NULL' because the default value will be used when that key column is not provided.
+		This effectivly means that later queries must consistently use the username (w.o domain)
+		in order to find a result to that particular subscriber query.
+		The main point is 'use_domain' can not be changed once the db_berkeley is setup.
+		</para>
+		
+	</section>
+	
+	<section>
+	<title>Database Schema and Metadata</title>
+	
+	<para>
+	All Berkeley DB tables are created via the kamdbctl script. 
+	This section provides details as to the content and 
+	format of the DB file upon creation.
+	</para>
+
+	<para>
+	Since the Berkeley DB stores key value pairs, the database is seeded 
+	with a few meta-data rows . The keys to these rows must begin with 'METADATA'. 
+	Here is an example of table meta-data, taken from the table 'version'.
+	</para>
+
+	<para>
+	Note on reserved character- 
+	The '|' pipe character is used as a record delimiter within the 
+	Berkeley DB implementation and must not be present in any DB field.
+	</para>
+
+	<example>
+	<title>METADATA_COLUMNS</title>
+	<programlisting format="linespecific">
+METADATA_COLUMNS
+table_name(str) table_version(int)
+METADATA_KEY
+0
+	</programlisting>
+	</example>
+
+	<para>
+	In the above example, the row METADATA_COLUMNS defines the column names 
+	and type, and the row METADATA_KEY defines which column(s) form the key. 
+	Here the value of 0 indicates that column 0 is the key(ie table_name). 
+	With respect to column types, the db_berkeley modules only has the following 
+	types: string, str, int, double, and datetime. The default type is string, 
+	and is used when one of the others is not specified. The columns of the 
+	meta-data are delimited by whitespace.
+	</para>
+
+	<para>
+	The actual column data is stored as a string value, and delimited by 
+	the '|' pipe character. Since the code tokenizes on this delimiter, 
+	it is important that this character not appear in any valid data field. 
+	The following is the output of the 'db_berkeley.sh dump version' command. 
+	It shows contents of table 'version' in plain text.
+	</para>
+	
+	<example>
+	<title>contents of version table</title>
+	<programlisting format="linespecific">
+VERSION=3
+format=print
+type=hash
+h_nelem=21
+db_pagesize=4096
+HEADER=END
+ METADATA_READONLY
+ 1
+ address|
+ address|3
+ aliases|
+ aliases|1004
+ dbaliases|
+ dbaliases|1
+ domain|
+ domain|1
+ gw_grp|
+ gw_grp|1
+ gw|
+ gw|4
+ speed_dial|
+ speed_dial|2
+ subscriber|
+ subscriber|6
+ uri|
+ uri|1
+ METADATA_COLUMNS
+ table_name(str) table_version(int)
+ METADATA_KEY
+ 0
+ acc|
+ acc|4
+ grp|
+ grp|2
+ lcr|
+ lcr|2
+ location|
+ location|1004
+ missed_calls|
+ missed_calls|3
+ re_grp|
+ re_grp|1
+ silo|
+ silo|5
+ trusted|
+ trusted|4
+ usr_preferences|
+ usr_preferences|2
+DATA=END
+	</programlisting>
+	</example>
+	</section>
+	
+	<section>
+	<title>METADATA_COLUMNS (required)</title>
+	<para>
+	The METADATA_COLUMNS row contains the column names and types. 
+	Each is space delimited. Here is an example of the data taken from table subscriber :
+	</para>
+	
+	<example>
+	<title>METADATA_COLUMNS</title>
+	<programlisting>
+METADATA_COLUMNS
+username(str) domain(str) password(str) ha1(str) ha1b(str) first_name(str) last_name(str) email_address(str) datetime_created(datetime) timezone(str) rpid(str)
+ 	</programlisting>
+	</example>
+	
+	<para>
+	Related (hardcoded) limitations:
+	<itemizedlist>
+		<listitem>
+			<para>maximum of 32 columns per table.</para>
+		</listitem>
+		
+		<listitem>
+			<para>maximum tablename size is 64.</para>
+		</listitem>
+		
+		<listitem>
+			<para>maximum data length is 2048</para>
+		</listitem>
+	</itemizedlist>
+	</para>
+	
+	<para>
+	Currently supporting these five types: str, datetime, int, double, string.
+	</para>
+	
+</section>
+
+	<section>
+	<title>METADATA_KEYS (required)</title>
+	<para>
+	The METADATA_KEYS row indicates the indexes of the key columns, 
+	with respect to the order specified in METADATA_COLUMNS. 
+	Here is an example taken from table subscriber that brings up a good point:
+	</para>
+	
+	<example>
+	<title>METADATA_KEYS</title>
+	<programlisting>
+ METADATA_KEY
+ 0 1
+ 	</programlisting>
+	</example>
+
+ 	<para>
+	The point is that both the username and domain name are require 
+	as the key to this record. Thus, usrloc modparam 
+	use_domain = 1 must be set for this to work.
+	</para>
+	
+	</section>
+
+	<section>
+	<title>METADATA_READONLY (optional)</title>
+	<para>
+	The METADATA_READONLY row contains a boolean 0 or 1. 
+	By default, its value is 0. On startup the DB will 
+	open initially as read-write (loads metadata) and then if this 
+	is set=1, it will close and reopen as read only (ro). 
+	I found this useful because readonly has impacts on the 
+	internal db locking etc.
+	</para>
+	
+	</section>
+
+	<section>
+	<title>METADATA_LOGFLAGS (optional)</title>
+	<para>
+	The METADATA_LOGFLAGS row contains a bitfield that customizes the 
+	journaling on a per table basis. If not present the default value 
+	is taken as 0. Here are the masks so far (taken from bdb_lib.h):
+	</para>
+	
+	<example>
+	<title>METADATA_LOGFLAGS</title>
+	<programlisting>
+#define JLOG_NONE 0
+#define JLOG_INSERT 1
+#define JLOG_DELETE 2
+#define JLOG_UPDATE 4
+#define JLOG_STDOUT 8
+#define JLOG_SYSLOG 16
+	</programlisting>
+	</example>
+	
+	<para>
+	This means that if you want to journal INSERTS to local file and syslog the value 
+	should be set to 1+16=17. Or if you do not want to journal at all, set this to 0.
+	</para>
+	
+	</section>
+	
+	<section>
+	<title>DB Maintaince Script : kamdbctl </title>
+	
+	<para>
+	Use the kamdbctl script for maintaining Kamailio Berkeley DB tables.
+	This script assumes you have DBENGINE and DB_PATH setup correctly in kamctlrc.
+	Note Unsupported commands are- backup, restore, migrate, copy, serweb.
+	<example>
+	<title>kamdbctl</title>
+	<programlisting>
+usage: kamdbctl create   
+       kamdbctl presence 
+       kamdbctl extra    
+       kamdbctl drop     
+       kamdbctl reinit   
+       kamdbctl bdb list         (lists the underlying db files in DB_PATH)
+       kamdbctl bdb cat       db (prints the contents of db file to STDOUT in plain-text)
+       kamdbctl bdb swap      db (installs db.new by db -> db.old; db.new -> db)
+       kamdbctl bdb append    db datafile (appends data to a new instance of db; output DB_PATH/db.new)
+       kamdbctl bdb newappend db datafile (appends data to a new instance of db; output DB_PATH/db.new)
+	</programlisting>
+	</example>
+	</para>
+	</section>
+	
+	<section>
+	<title>DB Recovery : kambdb_recover</title>
+	<para>
+	The db_berkeley module uses the Concurrent Data Store (CDS) architecture. 
+	As such, no transaction or journaling is provided by the DB natively. 
+	The application kambdb_recover is specifically written to recover data from 
+	journal files that Kamailio creates.  
+	The kambdb_recover application requires an additional text file that contains 
+	the table schema.
+	</para>
+	
+	<para>
+	The schema is loaded with the '-s' option and is required for all operations.
+	Provide the path to the db_berkeley plain-text schema files. By default, these
+	install to '/usr/local/share/kamailio/db_berkeley/kamailio/'.
+	</para>
+	
+	<para>
+	The '-h' home option is the DB_PATH path. Unlike the Berkeley utilities, 
+	this application does not look for the DB_PATH environment variable, 
+	so you have to specify it. If not specified, it will assume the current 
+	working directory. The last argument is the operation. 
+	There are fundamentally only two operations- create and recover. 
+	</para>
+	
+	<para>
+	The following illustrates the four operations available to the administrator.
+	<example>
+	<title>kambdb_recover usage</title>
+	<programlisting>
+usage: ./kambdb_recover -s schemadir [-h home] [-c tablename]
+	This will create a brand new DB file with metadata.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-C all]
+	This will create all the core tables, each with metadata.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-r journal-file]
+	This will rebuild a DB and populate it with operation from journal-file. 
+	The table name is embedded in the journal-file name by convention.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-R lastN]
+	This will iterate over all core tables enumerated. If journal files exist in 'home', 
+	a new DB file will be created and populated with the data found in the last N files. 
+	The files are 'replayed' in chronological order (oldest to newest). This 
+	allows the administrator to rebuild the db with a subset of all possible 
+	operations if needed. For example, you may only be interested in 
+	the last hours data in table location.
+	</programlisting>
+	</example>
+	</para>
+	
+	<para>
+	Important note- A corrupted DB file must be moved out of the way before kambdb_recover is executed.
+	</para>
+	
+	</section>
+	
+	<section>
+	<title>Known Limitations</title>
+	<para>
+	The Berkeley DB does not nativly support an autoincrement (or sequence) mechanism.
+	Consequently, this version does not support surragate keys in dbschema. These
+	are the id columns in the tables.
+	</para>
+	</section>
+	
+</chapter>
+

+ 1321 - 0
modules/db_berkeley/km_bdb_lib.c

@@ -0,0 +1,1321 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+
+#include "km_bdb_util.h"
+#include "km_bdb_lib.h"
+#include "km_bdb_val.h"
+
+static database_p *_cachedb = NULL;
+static db_parms_p _db_parms = NULL;
+
+/**
+ *
+ */
+int km_bdblib_init(db_parms_p _p) 
+{
+	if (!_cachedb)
+	{
+		_cachedb = pkg_malloc( sizeof(database_p) );
+		if (!_cachedb) 
+		{	LM_CRIT("not enough private memory\n");
+			return -1;
+		}
+		
+		*_cachedb = NULL;
+		
+		/*create default parms*/
+		db_parms_p dp = (db_parms_p) pkg_malloc( sizeof(db_parms_t) );
+		if (!dp) 
+		{	LM_CRIT("not enough private memory\n");
+			return -1;
+		}
+		
+		if(_p)
+		{
+			dp->cache_size  = _p->cache_size;
+			dp->auto_reload = _p->auto_reload;
+			dp->log_enable  = _p->log_enable;
+			dp->journal_roll_interval = _p->journal_roll_interval;
+		}
+		else
+		{
+			dp->cache_size = (4 * 1024 * 1024); //4Mb
+			dp->auto_reload = 0;
+			dp->log_enable = 0;
+			dp->journal_roll_interval = 3600;
+		}
+		
+		_db_parms = dp;
+	}
+	return 0;
+}
+
+
+/**
+ * close all DBs and then the DBENV; free all memory
+ */
+int km_bdblib_destroy(void)
+{
+	if (_cachedb)	db_free(*_cachedb);
+	if(_db_parms)	pkg_free(_db_parms);
+	return 0;
+}
+
+
+/** closes the underlying Berkeley DB.
+  assumes the lib data-structures are already initialzed;
+  used to sync and reload the db file.
+*/
+int km_bdblib_close(char* _n)
+{
+	str s;
+	int rc;
+	tbl_cache_p _tbc;
+	DB* _db = NULL;
+	DB_ENV* _env = NULL;
+	database_p _db_p = *_cachedb;
+	
+	if (!_cachedb || !_n)
+		return -1;
+	
+	rc = 0;
+	s.s = (char*)_n;
+	s.len = strlen(_n);
+	
+	if (_db_p)
+	{	
+		_env = _db_p->dbenv;
+		_tbc = _db_p->tables;
+LM_DBG("ENV %.*s \n"
+	, _db_p->name.len
+	, _db_p->name.s);
+		if(s.len == _db_p->name.len && 
+		!strncasecmp(s.s, _db_p->name.s, _db_p->name.len))
+		{
+			//close the whole dbenv
+			LM_DBG("ENV %.*s \n", s.len, s.s);
+			while(_tbc)
+			{
+				if(_tbc->dtp)
+				{
+					lock_get(&_tbc->dtp->sem);
+					_db = _tbc->dtp->db;
+					if(_db)
+						rc = _db->close(_db, 0);
+					if(rc != 0)
+						LM_CRIT("error closing %s\n", _tbc->dtp->name.s);
+					_tbc->dtp->db = NULL;
+					
+					lock_release(&_tbc->dtp->sem);
+				}
+				_tbc = _tbc->next;
+			}
+			_env->close(_env, 0);
+			_db_p->dbenv = NULL;
+			return 0;
+		}
+		
+		//close a particular db
+		while(_tbc)
+		{
+			if(_tbc->dtp)
+			{
+	LM_DBG("checking DB %.*s \n"
+		, _tbc->dtp->name.len
+		, _tbc->dtp->name.s);
+				
+				if(_tbc->dtp->name.len == s.len && 
+				!strncasecmp(_tbc->dtp->name.s, s.s, s.len ))
+				{
+					LM_DBG("DB %.*s \n", s.len, s.s);
+					lock_get(&_tbc->dtp->sem);
+					_db = _tbc->dtp->db;
+					if(_db)
+						rc = _db->close(_db, 0);
+					if(rc != 0)
+						LM_CRIT("error closing %s\n", _tbc->dtp->name.s);
+					_tbc->dtp->db = NULL;
+					lock_release(&_tbc->dtp->sem);
+					return 0;
+				}
+			}
+			_tbc = _tbc->next;
+		}
+	}
+	LM_DBG("DB not found %.*s \n", s.len, s.s);
+	return 1; /*table not found*/
+}
+
+/** opens the underlying Berkeley DB.
+  assumes the lib data-structures are already initialzed;
+  used to sync and reload the db file.
+*/
+int km_bdblib_reopen(char* _n)
+{
+	str s;
+	int rc, flags;
+	tbl_cache_p _tbc;
+	DB* _db = NULL;
+	DB_ENV* _env = NULL;
+	database_p _db_p = *_cachedb;
+	rc = flags = 0;
+	_tbc = NULL;
+	
+	if (!_cachedb || !_n)
+		return -1;
+
+	s.s = (char*)_n;
+	s.len = strlen(_n);
+	
+	if (_db_p)
+	{
+		_env = _db_p->dbenv;
+		_tbc = _db_p->tables;
+		
+		if(s.len ==_db_p->name.len && 
+		!strncasecmp(s.s, _db_p->name.s,_db_p->name.len))
+		{
+			//open the whole dbenv
+			LM_DBG("-- km_bdblib_reopen ENV %.*s \n", s.len, s.s);
+			if(!_db_p->dbenv)
+			{	rc = km_bdblib_create_dbenv(&_env, _n);
+				_db_p->dbenv = _env;
+			}
+			
+			if(rc!=0) return rc;
+			_env = _db_p->dbenv;
+			_tbc = _db_p->tables;
+
+			while(_tbc)
+			{
+				if(_tbc->dtp)
+				{
+					lock_get(&_tbc->dtp->sem);
+					if(!_tbc->dtp->db)
+					{
+						if ((rc = db_create(&_db, _env, 0)) != 0)
+						{	_env->err(_env, rc, "db_create");
+							LM_CRIT("error in db_create, db error: %s.\n",db_strerror(rc));
+							km_bdblib_recover(_tbc->dtp, rc);
+						}
+					}
+					
+					if ((rc = _db->open(_db, NULL, _n, NULL, DB_HASH, DB_CREATE, 0664)) != 0)
+					{	_db->dbenv->err(_env, rc, "DB->open: %s", _n);
+						LM_CRIT("error in db_open: %s.\n",db_strerror(rc));
+						km_bdblib_recover(_tbc->dtp, rc);
+					}
+					
+					_tbc->dtp->db = _db;
+					lock_release(&_tbc->dtp->sem);
+				}
+				_tbc = _tbc->next;
+			}
+			_env->close(_env, 0);
+			return rc;
+		}
+		
+		//open a particular db
+		while(_tbc)
+		{
+			if(_tbc->dtp)
+			{
+	LM_DBG("checking DB %.*s \n"
+		, _tbc->dtp->name.len
+		, _tbc->dtp->name.s);
+				
+				if(_tbc->dtp->name.len == s.len && 
+				!strncasecmp(_tbc->dtp->name.s, s.s, s.len ))
+				{
+					LM_DBG("DB %.*s \n", s.len, s.s);
+					lock_get(&_tbc->dtp->sem);
+					if(!_tbc->dtp->db) 
+					{
+						if ((rc = db_create(&_db, _env, 0)) != 0)
+						{	_env->err(_env, rc, "db_create");
+							LM_CRIT("error in db_create, db error: %s.\n",db_strerror(rc));
+							km_bdblib_recover(_tbc->dtp, rc);
+						}
+					}
+					
+					if ((rc = _db->open(_db, NULL, _n, NULL, DB_HASH, DB_CREATE, 0664)) != 0)
+					{	_db->dbenv->err(_env, rc, "DB->open: %s", _n);
+						LM_CRIT("bdb open: %s.\n",db_strerror(rc));
+						km_bdblib_recover(_tbc->dtp, rc);
+					}
+					_tbc->dtp->db = _db;
+					lock_release(&_tbc->dtp->sem);
+					return rc;
+				}
+			}
+			_tbc = _tbc->next;
+		}
+		
+	}
+	LM_DBG("DB not found %.*s \n", s.len, s.s);
+	return 1; /*table not found*/
+}
+
+
+/**
+ *
+ */
+int km_bdblib_create_dbenv(DB_ENV **_dbenv, char* _home)
+{
+	DB_ENV *env;
+	char *progname;
+	int rc, flags;
+	
+	progname = "openser";
+	
+	/* Create an environment and initialize it for additional error * reporting. */ 
+	if ((rc = db_env_create(&env, 0)) != 0) 
+	{
+		LM_ERR("db_env_create failed! bdb error: %s.\n", db_strerror(rc)); 
+		return (rc);
+	}
+ 
+	env->set_errpfx(env, progname);
+
+	/*  Specify the shared memory buffer pool cachesize */ 
+	if ((rc = env->set_cachesize(env, 0, _db_parms->cache_size, 0)) != 0) 
+	{
+		LM_ERR("dbenv set_cachsize failed! bdb error: %s.\n", db_strerror(rc));
+		env->err(env, rc, "set_cachesize"); 
+		goto err; 
+	}
+
+	/* Concurrent Data Store flags */
+	flags = DB_CREATE |
+		DB_INIT_CDB |
+		DB_INIT_MPOOL |
+		DB_THREAD;
+	
+	/* Transaction Data Store flags ; not supported yet */
+	/*
+	flags = DB_CREATE |
+		DB_RECOVER |
+		DB_INIT_LOG | 
+		DB_INIT_LOCK |
+		DB_INIT_MPOOL |
+		DB_THREAD |
+		DB_INIT_TXN;
+	*/
+	
+	/* Open the environment */ 
+	if ((rc = env->open(env, _home, flags, 0)) != 0) 
+	{ 
+		LM_ERR("dbenv is not initialized! bdb error: %s.\n",db_strerror(rc));
+		env->err(env, rc, "environment open: %s", _home); 
+		goto err; 
+	}
+	
+	*_dbenv = env;
+	return (0);
+
+err: (void)env->close(env, 0);
+	return (rc);
+}
+
+
+/**
+ */
+database_p km_bdblib_get_db(str *_s)
+{
+	int rc;
+	database_p _db_p=NULL;
+	char name[512];
+
+	if(!_s || !_s->s || _s->len<=0 || _s->len > 512)
+		return NULL;
+
+	if( !_cachedb)
+	{
+		LM_ERR("db_berkeley cache is not initialized! Check if you loaded db_berkeley "
+			"before any other module that uses it.\n");
+		return NULL;
+	}
+
+	_db_p = *_cachedb;
+	if(_db_p)
+	{
+		LM_DBG("db already cached!\n");
+		return _db_p;
+	}
+
+	if(!km_bdb_is_database(_s))
+	{	
+		LM_ERR("database [%.*s] does not exists!\n" ,_s->len , _s->s);
+		return NULL;
+	}
+
+	_db_p = (database_p)pkg_malloc(sizeof(database_t));
+	if(!_db_p)
+	{
+		LM_ERR("no private memory for dbenv_t.\n");
+		pkg_free(_db_p);
+		return NULL;
+	}
+
+	_db_p->name.s = (char*)pkg_malloc(_s->len*sizeof(char));
+	memcpy(_db_p->name.s, _s->s, _s->len);
+	_db_p->name.len = _s->len;
+
+	strncpy(name, _s->s, _s->len);
+	name[_s->len] = 0;
+
+	if ((rc = km_bdblib_create_dbenv(&(_db_p->dbenv), name)) != 0)
+	{
+		LM_ERR("km_bdblib_create_dbenv failed");
+		pkg_free(_db_p->name.s);
+		pkg_free(_db_p);
+		return NULL;
+	}
+
+	_db_p->tables=NULL;
+	*_cachedb = _db_p;
+
+	return _db_p;
+}
+
+
+/**
+ * look thru a linked list for the table. if dne, create a new one
+ * and add to the list
+*/
+tbl_cache_p km_bdblib_get_table(database_p _db, str *_s)
+{
+	tbl_cache_p _tbc = NULL;
+	table_p _tp = NULL;
+
+	if(!_db || !_s || !_s->s || _s->len<=0)
+		return NULL;
+
+	if(!_db->dbenv)
+	{
+		return NULL;
+	}
+
+	_tbc = _db->tables;
+	while(_tbc)
+	{
+		if(_tbc->dtp)
+		{
+
+			if(_tbc->dtp->name.len == _s->len 
+				&& !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len ))
+			{
+				return _tbc;
+			}
+		}
+		_tbc = _tbc->next;
+	}
+
+	_tbc = (tbl_cache_p)pkg_malloc(sizeof(tbl_cache_t));
+	if(!_tbc)
+		return NULL;
+
+	if(!lock_init(&_tbc->sem))
+	{
+		pkg_free(_tbc);
+		return NULL;
+	}
+
+	_tp = km_bdblib_create_table(_db, _s);
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("table: %.*s\n", _s->len, _s->s);
+#endif
+
+	if(!_tp)
+	{
+		LM_ERR("failed to create table.\n");
+		pkg_free(_tbc);
+		return NULL;
+	}
+
+	lock_get(&_tbc->sem);
+	_tbc->dtp = _tp;
+
+	if(_db->tables)
+		(_db->tables)->prev = _tbc;
+	
+	_tbc->next = _db->tables;
+	_db->tables = _tbc;
+	lock_release(&_tbc->sem);
+
+	return _tbc;
+}
+
+
+void km_bdblib_log(int op, table_p _tp, char* _msg, int len)
+{
+	if(!_tp || !len) 		return;
+	if(! _db_parms->log_enable) 	return;
+	if (_tp->logflags == JLOG_NONE)	return;
+	
+	if ((_tp->logflags & op) == op)
+	{	int op_len=7;
+		char buf[MAX_ROW_SIZE + op_len];
+		char *c;
+		time_t now = time(NULL);
+		
+		if( _db_parms->journal_roll_interval)
+		{
+			if((_tp->t) && (now - _tp->t) > _db_parms->journal_roll_interval)
+			{	/*try to roll logfile*/
+				if(km_bdblib_create_journal(_tp))
+				{
+					LM_ERR("Journaling has FAILED !\n");
+					return;
+				}
+			}
+		}
+		
+		c = buf;
+		switch (op)
+		{
+		case JLOG_INSERT:
+			strncpy(c, "INSERT|", op_len);
+			break;
+		case JLOG_UPDATE:
+			strncpy(c, "UPDATE|", op_len);
+			break;
+		case JLOG_DELETE:
+			strncpy(c, "DELETE|", op_len);
+			break;
+		}
+		
+		c += op_len;
+		strncpy(c, _msg, len);
+		c +=len;
+		*c = '\n';
+		c++;
+		*c = '\0';
+		
+		if ((_tp->logflags & JLOG_STDOUT) == JLOG_STDOUT)
+			puts(buf);
+		
+		if ((_tp->logflags & JLOG_SYSLOG) == JLOG_SYSLOG)
+			syslog(LOG_LOCAL6, buf);
+		
+		if(_tp->fp) 
+		{
+			if(!fputs(buf, _tp->fp) )
+				fflush(_tp->fp);
+		}
+	}
+}
+
+/**
+ * The function is called to create a handle to a db table.
+ * 
+ * On startup, we do not create any of the db handles.
+ * Instead it is done on first-use (lazy-initialized) to only create handles to 
+ * files (db) that we require.
+ * 
+ * There is one db file per openser table (eg. acc), and they should exist
+ * in your DB_PATH (refer to kamctlrc) directory.
+ *
+ * This function does _not_ create the underlying binary db tables.
+ * Creating the tables MUST be manually performed before 
+ * openser startup by 'kamdbctl create'
+ *
+ * Function returns NULL on error, which will cause openser to exit.
+ *
+ */
+table_p km_bdblib_create_table(database_p _db, str *_s)
+{
+
+	int rc,i,flags;
+	DB *bdb = NULL;
+	table_p tp = NULL;
+	char tblname[MAX_TABLENAME_SIZE]; 
+
+	if(!_db || !_db->dbenv)
+	{
+		LM_ERR("no database_p or dbenv.\n");
+		return NULL;
+	}
+
+	tp = (table_p)pkg_malloc(sizeof(table_t));
+	if(!tp)
+	{
+		LM_ERR("no private memory for table_t.\n");
+		return NULL;
+	}
+
+	if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0)
+	{ 
+		_db->dbenv->err(_db->dbenv, rc, "database create");
+		LM_ERR("error in db_create, bdb error: %s.\n",db_strerror(rc));
+		pkg_free(tp);
+		return NULL;
+	}
+
+	memset(tblname, 0, MAX_TABLENAME_SIZE);
+	strncpy(tblname, _s->s, _s->len);
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("CREATE TABLE = %s\n", tblname);
+#endif
+
+	flags = DB_THREAD;
+
+	if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0)
+	{ 
+		_db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
+		LM_ERR("bdb open failed: %s.\n",db_strerror(rc));
+		pkg_free(tp);
+		return NULL;
+	}
+
+	if(!lock_init(&tp->sem))
+	{
+		goto error;
+	}
+	
+	tp->name.s = (char*)pkg_malloc(_s->len*sizeof(char));
+	memcpy(tp->name.s, _s->s, _s->len);
+	tp->name.len = _s->len;
+	tp->db=bdb;
+	tp->ncols=0;
+	tp->nkeys=0;
+	tp->ro=0;    /*0=ReadWrite ; 1=ReadOnly*/
+	tp->ino=0;   /*inode*/
+	tp->logflags=0; /*bitmap; 4=Delete, 2=Update, 1=Insert, 0=None*/
+	tp->fp=0;
+	tp->t=0;
+	
+	for(i=0;i<MAX_NUM_COLS;i++)
+		tp->colp[i] = NULL;
+
+	/*load metadata; seeded\db_loaded when database are created*/
+	
+	/*initialize columns with metadata*/
+	rc = km_load_metadata_columns(tp);
+	if(rc!=0)
+	{
+		LM_ERR("FAILED to load METADATA COLS in table: %s.\n", tblname);
+		goto error;
+	}
+	
+	/*initialize columns default values from metadata*/
+	rc = km_load_metadata_defaults(tp);
+	if(rc!=0)
+	{
+		LM_ERR("FAILED to load METADATA DEFAULTS in table: %s.\n", tblname);
+		goto error;
+	}
+	
+	rc = km_load_metadata_keys(tp);
+	if(rc!=0)
+	{
+		LM_ERR("FAILED to load METADATA KEYS in table: %s.\n", tblname);
+		/*will have problems later figuring column types*/
+		goto error;
+	}
+
+	/*opened RW by default; Query to set the RO flag */
+	rc = km_load_metadata_readonly(tp);
+	if(rc!=0)
+	{
+		LM_INFO("No METADATA_READONLY in table: %s.\n", tblname);
+		/*non-critical; table will default to READWRITE*/
+	}
+
+	if(tp->ro)
+	{	
+		/*schema defines this table RO readonly*/
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("TABLE %.*s is changing to READONLY mode\n"
+			, tp->name.len, tp->name.s);
+#endif
+		
+		if ((rc = bdb->close(bdb,0)) != 0)
+		{ 
+			_db->dbenv->err(_db->dbenv, rc, "DB->close: %s", tblname);
+			LM_ERR("bdb close: %s.\n",db_strerror(rc));
+			goto error;
+		}
+		
+		bdb = NULL;
+		if ((rc = db_create(&bdb, _db->dbenv, 0)) != 0)
+		{ 
+			_db->dbenv->err(_db->dbenv, rc, "database create");
+			LM_ERR("error in db_create.\n");
+			goto error;
+		}
+		
+		flags = DB_THREAD | DB_RDONLY;
+		if ((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0)
+		{ 
+			_db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
+			LM_ERR("bdb open: %s.\n",db_strerror(rc));
+			goto error;
+		}
+		tp->db=bdb;
+	}
+	
+	/* set the journaling flags; flags indicate which operations
+	   need to be journalled. (e.g possible to only journal INSERT.)
+	*/
+	rc = km_load_metadata_logflags(tp);
+	if(rc!=0)
+		LM_INFO("No METADATA_LOGFLAGS in table: %s.\n", tblname);
+	
+	if ((tp->logflags & JLOG_FILE) == JLOG_FILE)
+		km_bdblib_create_journal(tp);
+	
+	return tp;
+	
+error:
+	if(tp) 
+	{
+		pkg_free(tp->name.s);
+		pkg_free(tp);
+	}
+	return NULL;
+}
+
+int km_bdblib_create_journal(table_p _tp)
+{
+	char *s;
+	char fn[1024];
+	char d[64];
+	FILE *fp = NULL;
+	struct tm *t;
+	int bl;
+	database_p _db_p = *_cachedb;
+	time_t tim = time(NULL);
+	
+	if(! _db_p || ! _tp) return -1;
+	if(! _db_parms->log_enable) return 0;
+	/* journal filename ; e.g. '/var/kamailio/db/location-YYYYMMDDhhmmss.jnl' */
+	s=fn;
+	strncpy(s, _db_p->name.s, _db_p->name.len);
+	s+=_db_p->name.len;
+	
+	*s = '/';
+	s++;
+	
+	strncpy(s, _tp->name.s, _tp->name.len);
+	s+=_tp->name.len;
+	
+	t = localtime( &tim );
+	bl=strftime(d,128,"-%Y%m%d%H%M%S.jnl",t);
+	strncpy(s, d, bl);
+	s+= bl;
+	*s = 0;
+	
+	if(_tp->fp)
+	{	/* must be rolling. */
+		if( fclose(_tp->fp) )
+		{	LM_ERR("Failed to Close Log in table: %.*s .\n", _tp->name.len,
+			 _tp->name.s);
+			return -1;
+		}
+	}
+	
+	if( (fp = fopen(fn, "w")) != NULL )
+	{
+		_tp->fp = fp;
+	}
+	else
+	{
+		LM_ERR("Failed to Open Log in table: %.*s .\n",_tp->name.len, _tp->name.s);
+		return -1;
+	}
+	
+	_tp->t = tim;
+	return 0;
+
+}
+
+int km_load_metadata_columns(table_p _tp)
+{
+	int ret,n,len;
+	char dbuf[MAX_ROW_SIZE];
+	char *s = NULL;
+	char cn[64], ct[16];
+	DB *db = NULL;
+	DBT key, data;
+	column_p col;
+	ret = n = len = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	if(_tp->ncols!=0)
+		return 0;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+
+	key.data = METADATA_COLUMNS;
+	key.size = strlen(METADATA_COLUMNS);
+
+	/*memory for the result*/
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{
+		db->err(db, ret, "km_load_metadata_columns DB->get failed");
+		LM_ERR("FAILED to find METADATA_COLUMNS in DB \n");
+		return -1;
+	}
+
+	/* eg: dbuf = "table_name(str) table_version(int)" */
+	s = strtok(dbuf, " ");
+	while(s!=NULL && n<MAX_NUM_COLS) 
+	{
+		/* eg: meta[0]=table_name  meta[1]=str */
+		sscanf(s,"%20[^(](%10[^)])[^\n]", cn, ct);
+		
+		/* create column*/
+		col = (column_p) pkg_malloc(sizeof(column_t));
+		if(!col)
+		{	LM_ERR("out of private memory \n");
+			return -1;
+		}
+		
+		/* set name*/
+		len = strlen( cn );
+		col->name.s = (char*)pkg_malloc(len * sizeof(char));
+		memcpy(col->name.s, cn, len );
+		col->name.len = len;
+		
+		/*set column type*/
+		if(strncmp(ct, "str", 3)==0)
+		{	col->type = DB1_STRING;
+		}
+		else if(strncmp(ct, "int", 3)==0)
+		{	col->type = DB1_INT;
+		}
+		else if(strncmp(ct, "double", 6)==0)
+		{	col->type = DB1_DOUBLE;
+		}
+		else if(strncmp(ct, "datetime", 8)==0)
+		{	col->type = DB1_DATETIME;
+		}
+		else
+		{	col->type = DB1_STRING;
+		}
+		
+		col->flag = 0;
+		_tp->colp[n] = col;
+		n++;
+		_tp->ncols++;
+		s=strtok(NULL, " ");
+	}
+
+	return 0;
+}
+
+int km_load_metadata_keys(table_p _tp)
+{
+	int ret,n,ci;
+	char dbuf[MAX_ROW_SIZE];
+	char *s = NULL;
+	DB *db = NULL;
+	DBT key, data;
+	ret = n = ci = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	key.data = METADATA_KEY;
+	key.size = strlen(METADATA_KEY);
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{
+		db->err(db, ret, "km_load_metadata_keys DB->get failed");
+		LM_ERR("FAILED to find METADATA in table \n");
+		return ret;
+	}
+	
+	s = strtok(dbuf, " ");
+	while(s!=NULL && n< _tp->ncols) 
+	{	ret = sscanf(s,"%i", &ci);
+		if(ret != 1) return -1;
+		if( _tp->colp[ci] ) 
+		{	_tp->colp[ci]->flag = 1;
+			_tp->nkeys++;
+		}
+		n++;
+		s=strtok(NULL, " ");
+	}
+
+	return 0;
+}
+
+
+int km_load_metadata_readonly(table_p _tp)
+{
+	int i, ret;
+	char dbuf[MAX_ROW_SIZE];
+
+	DB *db = NULL;
+	DBT key, data;
+	i = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	key.data = METADATA_READONLY;
+	key.size = strlen(METADATA_READONLY);
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{	return ret;
+	}
+	
+	if( 1 == sscanf(dbuf,"%i", &i) )
+		_tp->ro=(i>0)?1:0;
+	
+	return 0;
+}
+
+int km_load_metadata_logflags(table_p _tp)
+{
+	int i, ret;
+	char dbuf[MAX_ROW_SIZE];
+
+	DB *db = NULL;
+	DBT key, data;
+	i = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	key.data = METADATA_LOGFLAGS;
+	key.size = strlen(METADATA_LOGFLAGS);
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{	return ret;
+	}
+	
+	if( 1 == sscanf(dbuf,"%i", &i) )
+		_tp->logflags=i;
+	
+	return 0;
+}
+
+int km_load_metadata_defaults(table_p _tp)
+{
+	int ret,n,len;
+	char dbuf[MAX_ROW_SIZE];
+	char *s = NULL;
+	char cv[64];
+	DB *db = NULL;
+	DBT key, data;
+	column_p col;
+	ret = n = len = 0;
+	
+	if(!_tp || !_tp->db)
+		return -1;
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+
+	key.data = METADATA_DEFAULTS;
+	key.size = strlen(METADATA_DEFAULTS);
+
+	/*memory for the result*/
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 
+	{
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("NO DEFAULTS ; SETTING ALL columns to NULL! \n" );
+#endif
+
+		/*no defaults in DB; make some up.*/
+		for(n=0; n<_tp->ncols; n++)
+		{
+			col = _tp->colp[n];
+			if( col ) 
+			{	/*set all columns default value to 'NULL' */
+				len = strlen("NULL");
+				col->dv.s = (char*)pkg_malloc(len * sizeof(char));
+				memcpy(col->dv.s, "NULL", len);
+				col->dv.len = len;
+			}
+		}
+		return 0;
+	}
+	
+	/* use the defaults provided*/
+	s = strtok(dbuf, DELIM);
+	while(s!=NULL && n< _tp->ncols) 
+	{	ret = sscanf(s,"%s", cv);
+		if(ret != 1) return -1;
+		col = _tp->colp[n];
+		if( col ) 
+		{	/*set column default*/
+			len = strlen(s);
+			col->dv.s = (char*)pkg_malloc(len * sizeof(char));
+			memcpy(col->dv.s, cv, len);
+			col->dv.len = len;
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("COLUMN DEFAULT is %.*s for column[%.*s] \n"
+			, col->dv.len , ZSW(col->dv.s)
+			, col->name.len , ZSW(col->name.s)
+			);
+#endif
+
+		}
+		n++;
+		s=strtok(NULL, DELIM);
+	}
+	
+	return 0;
+}
+
+
+/*creates a composite key _k of length _klen from n values of _v;
+  provide your own initialized memory for target _k and _klen;
+  resulting value: _k = "KEY1 | KEY2"
+  ko = key only
+*/
+int km_bdblib_valtochar(table_p _tp, int* _lres, char* _k, int* _klen, db_val_t* _v, int _n, int _ko)
+{
+	char *p; 
+	char sk[MAX_ROW_SIZE]; // subkey(sk) val
+	char* delim = DELIM;
+	char* cNULL = "NULL";
+	int  len, total, sum;
+	int i, j, k;
+	p =  _k;
+	len = sum = total = 0;
+	i = j = k = 0;
+	
+	if(!_tp) return -1;
+	if(!_v || (_n<1) ) return -1;
+	if(!_k || !_klen ) return -1;
+	if( *_klen < 1)    return -1;
+	
+	memset(sk, 0, MAX_ROW_SIZE);
+	total = *_klen;
+	*_klen = 0; //sum
+	
+	if(! _lres)
+	{	
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("schema has NOT specified any keys! \n");
+#endif
+
+		/* schema has not specified keys
+		   just use the provided data in order provided
+		*/
+		for(i=0;i<_n;i++)
+		{	len = total - sum;
+			if ( km_bdb_val2str(&_v[i], sk, &len) != 0 ) 
+			{	LM_ERR("error building composite key \n");
+				return -2;
+			}
+
+			sum += len;
+			if(sum > total)
+			{	LM_ERR("Destination buffer too short for subval %s\n",sk);
+				return -2;
+			} 
+
+			/* write sk */
+			strncpy(p, sk, len);
+			p += len;
+			*_klen = sum;
+
+			sum += DELIM_LEN;
+			if(sum > total)
+			{	LM_ERR("Destination buffer too short for delim \n");
+				return -3;
+			}
+			
+			/* write delim */
+			strncpy(p, delim, DELIM_LEN);
+			p += DELIM_LEN;
+			*_klen = sum;;
+		}
+		return 0;
+	}
+
+
+	/*
+	  schema has specified keys
+	  verify all schema keys are provided
+	  use 'NULL' for those that are missing.
+	*/
+	for(i=0; i<_tp->ncols; i++)
+	{	/* i indexes columns in schema order */
+		if( _ko)
+		{	/* keymode; skip over non-key columns */
+			if( ! _tp->colp[i]->flag) 
+				continue; 
+		}
+		
+		for(j=0; j<_n; j++)
+		{	
+			/*
+			  j indexes the columns provided in _k
+			  which may be less than the total required by
+			  the schema. the app does not know the order
+			  of the columns in our schema!
+			 */
+			k = (_lres) ? _lres[j] : j;
+			
+			/*
+			 * k index will remap back to our schema order; like i
+			 */
+			if(i == k)
+			{
+				/*
+				 KEY was provided; append to buffer;
+				 _k[j] contains a key, but its a key that 
+				 corresponds to column k of our schema.
+				 now we know its a match, and we dont need
+				 index k for anything else
+				*/
+#ifdef BDB_EXTRA_DEBUG
+				LM_DBG("KEY PROVIDED[%i]: %.*s.%.*s \n", i 
+					, _tp->name.len , ZSW(_tp->name.s) 
+					, _tp->colp[i]->name.len, ZSW(_tp->colp[i]->name.s)
+				   );
+#endif
+
+				len = total - sum;
+				if ( km_bdb_val2str(&_v[j], sk, &len) != 0)
+				{	LM_ERR("Destination buffer too short for subval %s\n",sk);
+					return -4;
+				}
+				
+				sum += len;
+				if(sum > total)
+				{	LM_ERR("Destination buffer too short for subval %s\n",sk);
+					return -5;
+				}
+
+				strncpy(p, sk, len);
+				p += len;
+				*_klen = sum;
+
+				sum += DELIM_LEN;
+				if(sum > total)
+				{	LM_ERR("Destination buffer too short for delim \n");
+					return -5;
+				} 
+				
+				/* append delim */
+				strncpy(p, delim, DELIM_LEN);
+				p += DELIM_LEN;
+				*_klen = sum;
+				
+				
+				/* take us out of inner for loop
+				   and at the end of the outer loop
+				   to look for our next schema key
+				*/
+				goto next;
+			}
+			
+		}
+
+		/*
+		 NO KEY provided; use the column default value (dv)
+		     i.e _tp->colp[i]->dv
+		*/
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("Missing KEY[%i]: %.*s.%.*s using default [%.*s] \n", i
+			, _tp->name.len , ZSW(_tp->name.s) 
+			, _tp->colp[i]->name.len, ZSW(_tp->colp[i]->name.s)
+			, _tp->colp[i]->dv.len , ZSW(_tp->colp[i]->dv.s)
+		   );
+#endif
+		len = _tp->colp[i]->dv.len;
+		sum += len;
+		if(sum > total)
+		{	LM_ERR("Destination buffer too short for subval %s\n",cNULL);
+			return -5;
+		}
+		
+		strncpy(p, _tp->colp[i]->dv.s, len);
+		p += len;
+		*_klen = sum;
+		
+		sum += DELIM_LEN;
+		if(sum > total)
+		{	LM_ERR("Destination buffer too short for delim \n");
+			return -5;
+		} 
+		
+		strncpy(p, delim, DELIM_LEN);
+		p += DELIM_LEN;
+		*_klen = sum;
+next:
+		continue;
+	}
+
+
+
+	return 0;
+}
+
+
+/**
+ *
+ */
+int db_free(database_p _dbp)
+{
+	tbl_cache_p _tbc = NULL, _tbc0=NULL;
+	if(!_dbp)
+		return -1;
+
+	_tbc = _dbp->tables;
+
+	while(_tbc)
+	{
+		_tbc0 = _tbc->next;
+		tbl_cache_free(_tbc);
+		_tbc = _tbc0;
+	}
+	
+	if(_dbp->dbenv)
+		_dbp->dbenv->close(_dbp->dbenv, 0);
+	
+	if(_dbp->name.s)
+		pkg_free(_dbp->name.s);
+	
+	pkg_free(_dbp);
+
+	return 0;
+}
+
+
+/**
+ *
+ */
+int tbl_cache_free(tbl_cache_p _tbc)
+{
+	if(!_tbc)
+		return -1;
+	
+	lock_get(&_tbc->sem);
+	
+	if(_tbc->dtp)
+		tbl_free(_tbc->dtp);
+	
+	lock_destroy(&_tbc->sem);
+	pkg_free(_tbc);
+
+	return 0;
+}
+
+
+/**
+ * close DB (sync data to disk) and free mem
+ */
+int tbl_free(table_p _tp)
+{	int i;
+	if(!_tp)
+		return -1;
+
+	if(_tp->db)
+		_tp->db->close(_tp->db, 0);
+	
+	if(_tp->fp)
+		fclose(_tp->fp);
+
+	if(_tp->name.s)
+		pkg_free(_tp->name.s);
+	
+	for(i=0;i<_tp->ncols;i++)
+	{	if(_tp->colp[i])
+		{	pkg_free(_tp->colp[i]->name.s);
+			pkg_free(_tp->colp[i]->dv.s);
+			pkg_free(_tp->colp[i]);
+		}
+	}
+
+	pkg_free(_tp);
+	return 0;
+}
+
+int km_bdblib_recover(table_p _tp, int _rc)
+{
+	switch(_rc)
+	{
+		case DB_LOCK_DEADLOCK:
+		LM_ERR("DB_LOCK_DEADLOCK detected !!\n");
+		
+		case DB_RUNRECOVERY:
+		LM_ERR("DB_RUNRECOVERY detected !! \n");
+		km_bdblib_destroy();
+		exit(1);
+		break;
+	}
+	
+	return 0;
+}

+ 151 - 0
modules/db_berkeley/km_bdb_lib.h

@@ -0,0 +1,151 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#ifndef _KM_BDB_LIB_H_
+#define _KM_BDB_LIB_H_
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "../../str.h"
+#include "../../lib/srdb1/db.h"
+#include "../../lib/srdb1/db_val.h"
+#include "../../locking.h"
+
+/*max number of columns in a table*/
+#define MAX_NUM_COLS 32
+
+/*max char width of a table row*/
+#define MAX_ROW_SIZE 2048
+
+/*max char width of a table name*/
+#define MAX_TABLENAME_SIZE 64
+
+#define METADATA_COLUMNS "METADATA_COLUMNS"
+#define METADATA_KEY "METADATA_KEY"
+#define METADATA_READONLY "METADATA_READONLY"
+#define METADATA_LOGFLAGS "METADATA_LOGFLAGS"
+#define METADATA_DEFAULTS "METADATA_DEFAULTS"
+
+/*journal logging flag masks */
+#define JLOG_NONE   0
+#define JLOG_INSERT 1
+#define JLOG_DELETE 2
+#define JLOG_UPDATE 4
+#define JLOG_FILE   8
+#define JLOG_STDOUT 16
+#define JLOG_SYSLOG 32
+
+#define DELIM "|"
+#define DELIM_LEN (sizeof(DELIM)-1)
+
+typedef db_val_t bdb_val_t, *bdb_val_p;
+
+typedef struct _row
+{
+	bdb_val_p fields;
+	struct _row *prev;
+	struct _row *next;
+} row_t, *row_p;
+
+typedef struct _column
+{
+	str name;
+	str dv;     /* default value */
+	int type;
+	int flag;
+} column_t, *column_p;
+
+typedef struct _table
+{
+	str name;
+	DB *db;
+	gen_lock_t sem;
+	column_p colp [MAX_NUM_COLS];
+	int ncols;
+	int nkeys;
+	int ro;       /*db readonly flag*/
+	int logflags; /*flags indication what-where to journal log */
+	FILE* fp;     /*jlog file pointer */
+	time_t t;     /*jlog creation time */
+	ino_t ino;
+} table_t, *table_p;
+
+typedef struct _tbl_cache
+{
+	gen_lock_t sem;
+	table_p dtp;
+	struct _tbl_cache *prev;
+	struct _tbl_cache *next;
+} tbl_cache_t, *tbl_cache_p;
+
+typedef struct _database
+{
+	str name;
+	DB_ENV *dbenv;
+	tbl_cache_p tables;
+} database_t, *database_p;
+
+typedef struct _db_parms
+{
+	u_int32_t cache_size;
+	int auto_reload;
+	int log_enable;
+	int journal_roll_interval;
+} db_parms_t, *db_parms_p;
+
+
+int km_bdblib_init(db_parms_p _parms);
+int km_bdblib_destroy(void);
+int km_bdblib_close(char* _n);
+int km_bdblib_reopen(char* _n);
+int km_bdblib_recover(table_p _tp, int error_code);
+void km_bdblib_log(int op, table_p _tp, char* _msg, int len);
+int km_bdblib_create_dbenv(DB_ENV **dbenv, char* home);
+int km_bdblib_create_journal(table_p _tp);
+database_p  	km_bdblib_get_db(str *_s);
+tbl_cache_p 	km_bdblib_get_table(database_p _db, str *_s);
+table_p 	km_bdblib_create_table(database_p _db, str *_s);
+
+int db_free(database_p _dbp);
+int tbl_cache_free(tbl_cache_p _tbc);
+int tbl_free(table_p _tp);
+
+int km_load_metadata_columns(table_p _tp);
+int km_load_metadata_keys(table_p _tp);
+int km_load_metadata_readonly(table_p _tp);
+int km_load_metadata_logflags(table_p _tp);
+int km_load_metadata_defaults(table_p _tp);
+
+int km_bdblib_valtochar(table_p _tp, int* _lres, char* _k, int* _klen, db_val_t* _v, int _n, int _ko);
+
+#endif

+ 63 - 0
modules/db_berkeley/km_bdb_mi.c

@@ -0,0 +1,63 @@
+/*
+ * $Id:  $
+ *
+ * db_berkeley MI functions
+ *
+ * Copyright (C) 2007  Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2007-11-05  created (wiquan)
+ */
+
+
+#include "../../dprint.h"
+#include "../../lib/srdb1/db.h"
+#include "km_db_berkeley.h"
+#include "km_bdb_mi.h"
+
+
+/*
+ * MI function to reload db table or env
+ * expects 1 node: the tablename or dbenv name to reload
+ */
+struct mi_root* mi_bdb_reload(struct mi_root *cmd, void *param)
+{
+	struct mi_node *node;
+	str *db_path;
+	
+	node = cmd->node.kids;
+	if (node && node->next)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	
+	db_path = &node->value;
+	
+	if (!db_path || db_path->len == 0)
+		return init_mi_tree( 400, "bdb_reload missing db arg", 21);
+
+	if (bdb_reload(db_path->s) == 0) 
+	{
+		return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	} 
+	else 
+	{
+		return init_mi_tree( 500, "db_berkeley Reload Failed", 26);
+	}
+}
+

+ 36 - 0
modules/db_berkeley/km_bdb_mi.h

@@ -0,0 +1,36 @@
+/*
+ * $Id: $
+ *
+ * Header file for db_berkeley MI functions
+ *
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _KM_BDB_MI_H_
+#define _KM_BDB_MI_H_
+
+#include "../../lib/kmi/mi.h"
+
+#define MI_BDB_RELOAD "bdb_reload"
+
+struct mi_root* mi_bdb_reload(struct mi_root *cmd, void *param);
+
+
+#endif

+ 548 - 0
modules/db_berkeley/km_bdb_res.c

@@ -0,0 +1,548 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "../../mem/mem.h"
+#include "km_bdb_res.h"
+
+
+int bdb_get_columns(table_p _tp, db1_res_t* _res, int* _lres, int _nc)
+{
+	int col;
+
+	if (!_res) {
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+
+	if (_nc < 0 ) {
+		LM_ERR("_nc parameter cannot be negative \n");
+		return -1;
+	}
+    /* the number of rows (tuples) in the query result. */
+	RES_NUM_ROWS(_res) = 1;
+
+	if (!_lres)
+		_nc = _tp->ncols;
+
+	/* Save number of columns in the result structure */
+	RES_COL_N(_res) = _nc;
+
+	if (db_allocate_columns(_res, RES_COL_N(_res)) != 0) {
+		LM_ERR("could not allocate columns");
+		return -2;
+	}
+
+	/*
+	 * For each column both the name and the data type are saved.
+	 */
+	for(col = 0; col < RES_COL_N(_res); col++) {
+		column_p cp = NULL;
+		cp = (_lres) ? _tp->colp[_lres[col]] : _tp->colp[col];
+
+		RES_NAMES(_res)[col] = (str*)pkg_malloc(sizeof(str));
+		if (! RES_NAMES(_res)[col]) {
+			LM_ERR("no private memory left\n");
+			db_free_columns(_res);
+			return -3;
+		}
+		LM_DBG("allocate %lu bytes for RES_NAMES[%d] at %p\n",
+			(unsigned long)sizeof(str), col, RES_NAMES(_res)[col]);
+
+		/* The pointer that is here returned is part of the result structure. */
+		RES_NAMES(_res)[col]->s = cp->name.s;
+		RES_NAMES(_res)[col]->len = cp->name.len;
+
+		LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_res)[col]
+			, col, RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s);
+
+		RES_TYPES(_res)[col] = cp->type;
+	}
+	return 0;
+}
+
+
+
+/**
+ * Convert rows from Berkeley DB to db API representation
+ */
+int bdb_convert_row(db1_res_t* _res, char *bdb_result, int* _lres)
+{
+	int col, len, i, j;
+	char **row_buf, *s;
+	db_row_t* row = NULL;
+	col = len = i = j = 0;
+	
+	if (!_res) {
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+
+	/* Allocate a single row structure */
+	len = sizeof(db_row_t);
+	row = (db_row_t*)pkg_malloc(len);
+	if (!row) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	LM_DBG("allocate %d bytes for row %p\n", len, row);
+	memset(row, 0, len);
+	RES_ROWS(_res) = row;
+	
+	/* Save the number of rows in the current fetch */
+	RES_ROW_N(_res) = 1;
+
+	if (db_allocate_row(_res, row) != 0) {
+		LM_ERR("could not allocate row");
+		return -2;
+	}
+	/*
+	 * Allocate an array of pointers one per column.
+	 * It that will be used to hold the address of the string representation of each column.
+	 */
+	len = sizeof(char *) * RES_COL_N(_res);
+	row_buf = (char **)pkg_malloc(len);
+	if (!row_buf) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_res), len, row_buf);
+	memset(row_buf, 0, len);
+
+	/*populate the row_buf with bdb_result*/
+	/*bdb_result is memory from our callers stack so we copy here*/
+	s = strtok(bdb_result, DELIM);
+	while( s!=NULL)
+	{
+		if(_lres) {
+			/*only requested cols (_c was specified)*/
+			for(i=0; i<ROW_N(row); i++)
+			{	if (col == _lres[i]) {
+					len = strlen(s);
+					row_buf[i] = pkg_malloc(len+1);
+					if (!row_buf[i]) {
+						LM_ERR("no private memory left\n");
+						return -1;
+					}
+					LM_DBG("allocated %d bytes for row_buf[%d] at %p\n", len, i, row_buf[i]);
+					memset(row_buf[i], 0, len+1);
+					strncpy(row_buf[i], s, len);
+				}
+				
+			}
+		}
+		else {
+			len = strlen(s);
+			row_buf[col] = pkg_malloc(len+1);
+			if (!row_buf[col]) {
+				LM_ERR("no private memory left\n");
+				return -1;
+			}
+				LM_DBG("allocated %d bytes for row_buf[%d] at %p\n", len, col, row_buf[col]);
+			memset(row_buf[col], 0, len+1);
+			strncpy(row_buf[col], s, len);
+		}
+		s = strtok(NULL, DELIM);
+		col++;
+	}
+
+	/*do the type conversion per col*/
+        for(col = 0; col < ROW_N(row); col++) {
+		/*skip the unrequested cols (as already specified)*/
+		if(!row_buf[col])  continue;
+
+		/* Convert the string representation into the value representation */
+		if (bdb_str2val(RES_TYPES(_res)[col], &(ROW_VALUES(row)[col])
+				, row_buf[col], strlen(row_buf[col])) < 0) {
+			LM_ERR("while converting value\n");
+			LM_DBG("freeing row at %p\n", row);
+			db_free_row(row);
+			return -3;
+		}
+	}
+
+	/* pkg_free() must be done for the above allocations now that the row has been converted.
+	 * During bdb_convert_row (and subsequent bdb_str2val) processing, data types that don't need to be
+	 * converted (namely STRINGS) have their addresses saved.  These data types should not have
+	 * their pkg_malloc() allocations freed here because they are still needed.  However, some data types
+	 * (ex: INT, DOUBLE) should have their pkg_malloc() allocations freed because during the conversion
+	 * process, their converted values are saved in the union portion of the db_val_t structure.
+	 *
+	 * Warning: when the converted row is no longer needed, the data types whose addresses
+	 * were saved in the db_val_t structure must be freed or a memory leak will happen.
+	 * This processing should happen in the db_free_row() subroutine.  The caller of
+	 * this routine should ensure that db_free_rows(), db_free_row() or db_free_result()
+	 * is eventually called.
+	 */
+	for (col = 0; col < RES_COL_N(_res); col++) {
+		switch (RES_TYPES(_res)[col]) 
+		{
+			case DB1_STRING:
+			case DB1_STR:
+				break;
+			default:
+			LM_DBG("col[%d] Col[%.*s] Type[%d] Freeing row_buf[%p]\n", col
+				, RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s,
+				  RES_TYPES(_res)[col], row_buf[col]);
+			LM_DBG("freeing row_buf[%d] at %p\n", col, row_buf[col]);
+			pkg_free(row_buf[col]);
+		}
+		/* The following housekeeping may not be technically required, but it is a good practice
+		 * to NULL pointer fields that are no longer valid.  Note that DB1_STRING fields have not
+		 * been pkg_free(). NULLing DB1_STRING fields would normally not be good to do because a memory
+		 * leak would occur.  However, the pg_convert_row() routine has saved the DB1_STRING pointer
+		 * in the db_val_t structure.  The db_val_t structure will eventually be used to pkg_free()
+		 * the DB1_STRING storage.
+		 */
+		row_buf[col] = (char *)NULL;
+	}
+	LM_DBG("freeing row buffer at %p\n", row_buf);
+	pkg_free(row_buf);
+	row_buf = NULL;
+
+	return 0;
+
+}
+
+/*rx is row index*/
+int bdb_append_row(db1_res_t* _res, char *bdb_result, int* _lres, int _rx)
+{
+	int col, len, i, j;
+	char **row_buf, *s;
+	db_row_t* row = NULL;
+	col = len = i = j = 0;
+	
+	if (!_res) {
+		LM_ERR("invalid parameter");
+		return -1;
+	}
+	
+	row = &(RES_ROWS(_res)[_rx]);
+	
+	if (db_allocate_row(_res, row) != 0) {
+		LM_ERR("could not allocate row");
+		return -2;
+	}
+	
+	/*
+	 * Allocate an array of pointers one per column.
+	 * It that will be used to hold the address of the string representation of each column.
+	 */
+	len = sizeof(char *) * RES_COL_N(_res);
+	row_buf = (char **)pkg_malloc(len);
+	if (!row_buf) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_res), len, row_buf);
+	memset(row_buf, 0, len);
+	
+	/*populate the row_buf with bdb_result*/
+	/*bdb_result is memory from our callers stack so we copy here*/
+	s = strtok(bdb_result, DELIM);
+	while( s!=NULL)
+	{	
+		if(_lres) {
+			/*only requested cols (_c was specified)*/
+			for(i=0; i<ROW_N(row); i++) {
+				if (col == _lres[i]) {
+					len = strlen(s);
+					row_buf[i] = pkg_malloc(len+1);
+					if (!row_buf[i]) {
+						LM_ERR("no private memory left\n");
+						return -1;
+					}
+					memset(row_buf[i], 0, len+1);
+					strncpy(row_buf[i], s, len);
+				}
+			}
+		}
+		else {
+			len = strlen(s);
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("col[%i] = [%.*s]\n", col , len, s );
+#endif
+
+			row_buf[col] = (char*)pkg_malloc(len+1);
+			if (!row_buf[col]) {
+				LM_ERR("no private memory left\n");
+				return -1;
+			}
+			memset(row_buf[col], 0, len+1);
+			strncpy(row_buf[col], s, len);
+		}
+		s = strtok(NULL, DELIM);
+		col++;
+	}
+	
+	/*do the type conversion per col*/
+	for(col = 0; col < ROW_N(row); col++) {
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("tc 1: col[%i] == ", col );
+#endif
+
+		/*skip the unrequested cols (as already specified)*/
+		if(!row_buf[col])  continue;
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("tc 2: col[%i] \n", col );
+#endif
+
+		/* Convert the string representation into the value representation */
+		if (bdb_str2val(RES_TYPES(_res)[col], &(ROW_VALUES(row)[col])
+				, row_buf[col], strlen(row_buf[col])) < 0) {
+			LM_ERR("while converting value\n");
+			LM_DBG("freeing row at %p\n", row);
+			db_free_row(row);
+			return -3;
+		}
+	}
+
+	/* pkg_free() must be done for the above allocations now that the row has been converted.
+	 * During bdb_convert_row (and subsequent bdb_str2val) processing, data types that don't need to be
+	 * converted (namely STRINGS) have their addresses saved.  These data types should not have
+	 * their pkg_malloc() allocations freed here because they are still needed.  However, some data types
+	 * (ex: INT, DOUBLE) should have their pkg_malloc() allocations freed because during the conversion
+	 * process, their converted values are saved in the union portion of the db_val_t structure.
+	 *
+	 * Warning: when the converted row is no longer needed, the data types whose addresses
+	 * were saved in the db_val_t structure must be freed or a memory leak will happen.
+	 * This processing should happen in the db_free_row() subroutine.  The caller of
+	 * this routine should ensure that db_free_rows(), db_free_row() or db_free_result()
+	 * is eventually called.
+	 */
+	for (col = 0; col < RES_COL_N(_res); col++) {
+		if (RES_TYPES(_res)[col] != DB1_STRING) {
+			LM_DBG("col[%d] Col[%.*s] Type[%d] Freeing row_buf[%p]\n", col
+				, RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s,
+				  RES_TYPES(_res)[col], row_buf[col]);
+			LM_DBG("freeing row_buf[%d] at %p\n", col, row_buf[col]);
+			pkg_free(row_buf[col]);
+		}
+		/* The following housekeeping may not be technically required, but it is a good practice
+		 * to NULL pointer fields that are no longer valid.  Note that DB1_STRING fields have not
+		 * been pkg_free(). NULLing DB1_STRING fields would normally not be good to do because a memory
+		 * leak would occur.  However, the pg_convert_row() routine has saved the DB1_STRING pointer
+		 * in the db_val_t structure.  The db_val_t structure will eventually be used to pkg_free()
+		 * the DB1_STRING storage.
+		 */
+		row_buf[col] = (char *)NULL;
+	}
+	LM_DBG("freeing row buffer at %p\n", row_buf);
+	pkg_free(row_buf);
+	row_buf = NULL;
+	return 0;
+}
+
+
+
+int* bdb_get_colmap(table_p _dtp, db_key_t* _k, int _n)
+{
+	int i, j, *_lref=NULL;
+	
+	if(!_dtp || !_k || _n < 0)
+		return NULL;
+	
+	_lref = (int*)pkg_malloc(_n*sizeof(int));
+	if(!_lref)
+		return NULL;
+	
+	for(i=0; i < _n; i++)
+	{
+		for(j=0; j<_dtp->ncols; j++) {
+			if(_k[i]->len==_dtp->colp[j]->name.len
+			&& !strncasecmp(_k[i]->s, _dtp->colp[j]->name.s,
+						_dtp->colp[j]->name.len)) {
+				_lref[i] = j;
+				break;
+			}
+		}
+		if(i>=_dtp->ncols) {
+			LM_DBG("ERROR column <%.*s> not found\n", _k[i]->len, _k[i]->s);
+			pkg_free(_lref);
+			return NULL;
+		}
+	}
+	return _lref;
+}
+
+
+int bdb_is_neq_type(db_type_t _t0, db_type_t _t1)
+{
+	if(_t0 == _t1)	return 0;
+	
+	switch(_t1)
+	{
+		case DB1_INT:
+			if(_t0==DB1_DATETIME || _t0==DB1_BITMAP)
+				return 0;
+		case DB1_BIGINT:
+				LM_ERR("BIGINT not supported");
+				return 0;
+		case DB1_DATETIME:
+			if(_t0==DB1_INT)
+				return 0;
+			if(_t0==DB1_BITMAP)
+				return 0;
+		case DB1_DOUBLE:
+			break;
+		case DB1_STRING:
+			if(_t0==DB1_STR)
+				return 0;
+		case DB1_STR:
+			if(_t0==DB1_STRING || _t0==DB1_BLOB)
+				return 0;
+		case DB1_BLOB:
+			if(_t0==DB1_STR)
+				return 0;
+		case DB1_BITMAP:
+			if (_t0==DB1_INT)
+				return 0;
+	}
+	return 1;
+}
+
+
+/*
+*/
+int bdb_row_match(db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n, db1_res_t* _r, int* _lkey )
+{
+	int i, res;
+	db_row_t* row = NULL;
+	
+	if(!_r || !_lkey)
+		return 1;
+	
+	row = RES_ROWS(_r);
+	
+	for(i=0; i<_n; i++) {
+		res = bdb_cmp_val(&(ROW_VALUES(row)[_lkey[i]]), &_v[i]);
+
+		if(!_op || !strcmp(_op[i], OP_EQ)) {
+			if(res!=0)
+				return 0;
+		} else {
+		if(!strcmp(_op[i], OP_LT)) {
+			if(res!=-1)
+				return 0;
+		} else {
+		if(!strcmp(_op[i], OP_GT)) {
+			if(res!=1)
+				return 0;
+		} else {
+		if(!strcmp(_op[i], OP_LEQ)) {
+			if(res==1)
+				return 0;
+		} else {
+		if(!strcmp(_op[i], OP_GEQ)) {
+			if(res==-1)
+				return 0;
+		} else {
+			return res;
+		}}}}}
+	}
+	
+	return 1;
+}
+
+/*
+*/
+int bdb_cmp_val(db_val_t* _vp, db_val_t* _v)
+{
+	int _l, _n;
+	
+	if(!_vp && !_v)
+		return 0;
+	if(!_v)
+		return 1;
+	if(!_vp)
+		return -1;
+	if(_vp->nul && _v->nul)
+		return 0;
+	if(_v->nul)
+		return 1;
+	if(_vp->nul)
+		return -1;
+	
+	switch(VAL_TYPE(_v))
+	{
+		case DB1_INT:
+			return (_vp->val.int_val<_v->val.int_val)?-1:
+					(_vp->val.int_val>_v->val.int_val)?1:0;
+		case DB1_BIGINT:
+			LM_ERR("BIGINT not supported");
+			return -1;
+		case DB1_DOUBLE:
+			return (_vp->val.double_val<_v->val.double_val)?-1:
+					(_vp->val.double_val>_v->val.double_val)?1:0;
+		case DB1_DATETIME:
+			return (_vp->val.int_val<_v->val.time_val)?-1:
+					(_vp->val.int_val>_v->val.time_val)?1:0;
+		case DB1_STRING:
+			_l = strlen(_v->val.string_val);
+			_l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l;
+			_n = strncasecmp(_vp->val.str_val.s, _v->val.string_val, _l);
+			if(_n)
+				return _n;
+			if(_vp->val.str_val.len == strlen(_v->val.string_val))
+				return 0;
+			if(_l==_vp->val.str_val.len)
+				return -1;
+			return 1;
+		case DB1_STR:
+			_l = _v->val.str_val.len;
+			_l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l;
+			_n = strncasecmp(_vp->val.str_val.s, _v->val.str_val.s, _l);
+			if(_n)
+				return _n;
+			if(_vp->val.str_val.len == _v->val.str_val.len)
+				return 0;
+			if(_l==_vp->val.str_val.len)
+				return -1;
+			return 1;
+		case DB1_BLOB:
+			_l = _v->val.blob_val.len;
+			_l = (_l>_vp->val.str_val.len)?_vp->val.str_val.len:_l;
+			_n = strncasecmp(_vp->val.str_val.s, _v->val.blob_val.s, _l);
+			if(_n)
+				return _n;
+			if(_vp->val.str_val.len == _v->val.blob_val.len)
+				return 0;
+			if(_l==_vp->val.str_val.len)
+				return -1;
+			return 1;
+		case DB1_BITMAP:
+			return (_vp->val.int_val<_v->val.bitmap_val)?-1:
+				(_vp->val.int_val>_v->val.bitmap_val)?1:0;
+	}
+	return -2;
+}

+ 61 - 0
modules/db_berkeley/km_bdb_res.h

@@ -0,0 +1,61 @@
+/*
+ * $Id$
+ *
+ * sleepycat module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#ifndef _KM_BDB_RES_H_
+#define _KM_BDB_RES_H_
+
+#include "../../lib/srdb1/db_op.h"
+#include "../../lib/srdb1/db_res.h"
+#include "../../lib/srdb1/db_con.h"
+#include "km_bdb_lib.h"
+#include "km_bdb_val.h"
+
+typedef struct _con
+{
+	database_p con;
+	db1_res_t*  res;
+	row_p row;
+} bdb_con_t, *bdb_con_p;
+
+#define BDB_CON_CONNECTION(db_con) (((bdb_con_p)((db_con)->tail))->con)
+#define BDB_CON_RESULT(db_con)     (((bdb_con_p)((db_con)->tail))->res)
+#define BDB_CON_ROW(db_con)        (((bdb_con_p)((db_con)->tail))->row)
+
+int bdb_get_columns(table_p _tp, db1_res_t* _res, int* _lres, int _nc);
+int bdb_convert_row( db1_res_t* _res, char *bdb_result, int* _lres);
+int bdb_append_row(db1_res_t* _res, char *bdb_result, int* _lres, int _rx);
+int* bdb_get_colmap(table_p _tp, db_key_t* _k, int _n);
+
+int bdb_is_neq_type(db_type_t _t0, db_type_t _t1);
+int bdb_row_match(db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n, db1_res_t* _r, int* lkey );
+int bdb_cmp_val(db_val_t* _vp, db_val_t* _v);
+
+#endif
+

+ 55 - 0
modules/db_berkeley/km_bdb_util.c

@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "km_bdb_util.h"
+
+/**
+ *
+ */
+int km_bdb_is_database(str *_s)
+{
+	DIR *dirp = NULL;
+	char buf[512];
+	
+	if(!_s || !_s->s || _s->len <= 0 || _s->len > 510)
+		return 0;
+	strncpy(buf, _s->s, _s->len);
+	buf[_s->len] = 0;
+	dirp = opendir(buf);
+	if(!dirp)
+		return 0;
+	closedir(dirp);
+
+	return 1;
+}
+

+ 39 - 0
modules/db_berkeley/km_bdb_util.h

@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#ifndef _KM_BDB_UTIL_H_
+#define _KM_BDB_UTIL_H_
+
+#include "../../str.h"
+
+int km_bdb_is_database(str *);
+
+#endif
+

+ 286 - 0
modules/db_berkeley/km_bdb_val.c

@@ -0,0 +1,286 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+ 
+
+#include "../../lib/srdb1/db_val.h"
+#include "../../lib/srdb1/db_ut.h"
+#include "km_db_berkeley.h"
+#include "km_bdb_res.h"
+#include "km_bdb_val.h"
+#include <string.h>
+
+/**
+ * A copy of db_ut::db_time2str EXCEPT does not wrap the date in single-quotes
+ *
+ * Convert a time_t value to string (w.o single-quote)
+ * \param _v source value
+ * \param _s target string
+ * \param _l available length and target length
+ * \return -1 on error, 0 on success
+ * \todo This functions add quotes to the time value. This
+ * should be done in the val2str function, as some databases
+ * like db_berkeley don't need or like this at all.
+ */
+inline int km_bdb_time2str(time_t _v, char* _s, int* _l)
+{
+	struct tm* t;
+	int l;
+
+	if ((!_s) || (!_l) || (*_l < 2)) {
+		LM_ERR("Invalid parameter value\n");
+		return -1;
+	}
+
+//	*_s++ = '\'';
+
+	/* Convert time_t structure to format accepted by the database */
+	t = localtime(&_v);
+	l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", t);
+
+	if (l == 0) {
+		LM_ERR("Error during time conversion\n");
+		/* the value of _s is now unspecified */
+		_s = NULL;
+		_l = 0;
+		return -1;
+	}
+	*_l = l;
+
+//	*(_s + l) = '\'';
+//	*_l = l + 2;
+	return 0;
+}
+
+/**
+ * Does not copy strings
+ */
+int bdb_str2val(db_type_t _t, db_val_t* _v, char* _s, int _l)
+{
+
+	static str dummy_string = {"", 0};
+
+	if(!_s)
+	{
+		memset(_v, 0, sizeof(db_val_t));
+		/* Initialize the string pointers to a dummy empty
+		 * string so that we do not crash when the NULL flag
+		 * is set but the module does not check it properly
+		 */
+		VAL_STRING(_v) = dummy_string.s;
+		VAL_STR(_v) = dummy_string;
+		VAL_BLOB(_v) = dummy_string;
+		VAL_TYPE(_v) = _t;
+		VAL_NULL(_v) = 1;
+		return 0;
+	}
+	VAL_NULL(_v) = 0;
+
+	switch(_t) {
+	case DB1_INT:
+		if (db_str2int(_s, &VAL_INT(_v)) < 0) {
+			LM_ERR("Error while converting INT value from string\n");
+			return -2;
+		} else {
+			VAL_TYPE(_v) = DB1_INT;
+			return 0;
+		}
+		break;
+
+	case DB1_BIGINT:
+			LM_ERR("BIGINT not supported");
+			return -1;
+
+	case DB1_BITMAP:
+		if (db_str2int(_s, &VAL_INT(_v)) < 0) {
+			LM_ERR("Error while converting BITMAP value from string\n");
+			return -3;
+		} else {
+			VAL_TYPE(_v) = DB1_BITMAP;
+			return 0;
+		}
+		break;
+
+	case DB1_DOUBLE:
+		if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) {
+			LM_ERR("Error while converting DOUBLE value from string\n");
+			return -4;
+		} else {
+			VAL_TYPE(_v) = DB1_DOUBLE;
+			return 0;
+		}
+		break;
+
+	case DB1_STRING:
+		VAL_STRING(_v) = _s;
+		VAL_TYPE(_v) = DB1_STRING;
+		VAL_FREE(_v) = 1;
+		
+		if( strlen(_s)==4 && !strncasecmp(_s, "NULL", 4) )
+			VAL_NULL(_v) = 1;
+		
+		return 0;
+
+	case DB1_STR:
+		VAL_STR(_v).s = (char*)_s;
+		VAL_STR(_v).len = _l;
+		VAL_TYPE(_v) = DB1_STR;
+		VAL_FREE(_v) = 1;
+
+		if( strlen(_s)==4 && !strncasecmp(_s, "NULL", 4) )
+			VAL_NULL(_v) = 1;
+
+		return 0;
+
+	case DB1_DATETIME:
+		if (db_str2time(_s, &VAL_TIME(_v)) < 0) {
+			LM_ERR("Error converting datetime\n");
+			return -5;
+		} else {
+			VAL_TYPE(_v) = DB1_DATETIME;
+			return 0;
+		}
+		break;
+
+	case DB1_BLOB:
+		VAL_BLOB(_v).s = _s;
+		VAL_TYPE(_v) = DB1_BLOB;
+		LM_DBG("got blob len %d\n", _l);
+		return 0;
+	}
+
+	return -6;
+}
+
+
+/*
+ * Used when converting result from a query
+ */
+int km_bdb_val2str(db_val_t* _v, char* _s, int* _len)
+{
+	int l;
+
+	if (VAL_NULL(_v)) 
+	{
+		*_len = snprintf(_s, *_len, "NULL");
+		return 0;
+	}
+	
+	switch(VAL_TYPE(_v)) {
+	case DB1_INT:
+		if (db_int2str(VAL_INT(_v), _s, _len) < 0) {
+			LM_ERR("Error while converting int to string\n");
+			return -2;
+		} else {
+			LM_DBG("Converted int to string\n");
+			return 0;
+		}
+		break;
+
+	case DB1_BITMAP:
+		if (db_int2str(VAL_INT(_v), _s, _len) < 0) {
+			LM_ERR("Error while converting bitmap to string\n");
+			return -3;
+		} else {
+			LM_DBG("Converted bitmap to string\n");
+			return 0;
+		}
+		break;
+
+	case DB1_DOUBLE:
+		if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) {
+			LM_ERR("Error while converting double  to string\n");
+			return -3;
+		} else {
+			LM_DBG("Converted double to string\n");
+			return 0;
+		}
+		break;
+
+	case DB1_STRING:
+		l = strlen(VAL_STRING(_v));
+		if (*_len < l ) 
+		{	LM_ERR("Destination buffer too short for string\n");
+			return -4;
+		} 
+		else 
+		{	LM_DBG("Converted string to string\n");
+			strncpy(_s, VAL_STRING(_v) , l);
+			_s[l] = 0;
+			*_len = l;
+			return 0;
+		}
+		break;
+
+	case DB1_STR:
+		l = VAL_STR(_v).len;
+		if (*_len < l) 
+		{
+			LM_ERR("Destination buffer too short for str\n");
+			return -5;
+		} 
+		else 
+		{
+			LM_DBG("Converted str to string\n");
+			strncpy(_s, VAL_STR(_v).s , VAL_STR(_v).len);
+			*_len = VAL_STR(_v).len;
+			return 0;
+		}
+		break;
+
+	case DB1_DATETIME:
+		if (km_bdb_time2str(VAL_TIME(_v), _s, _len) < 0) {
+			LM_ERR("Error while converting time_t to string\n");
+			return -6;
+		} else {
+			LM_DBG("Converted time_t to string\n");
+			return 0;
+		}
+		break;
+
+	case DB1_BLOB:
+		l = VAL_BLOB(_v).len;
+		if (*_len < l) 
+		{
+			LM_ERR("Destination buffer too short for blob\n");
+			return -7;
+		} 
+		else 
+		{
+			LM_DBG("Converting BLOB [%s]\n", _s);
+			_s = VAL_BLOB(_v).s;
+			*_len = 0;
+			return -8;
+		}
+		break;
+
+	default:
+		LM_DBG("Unknown data type\n");
+		return -8;
+	}
+}

+ 42 - 0
modules/db_berkeley/km_bdb_val.h

@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#ifndef _KM_BDB_VAL_H_
+#define _KM_BDB_VAL_H_
+
+#include "../../lib/srdb1/db_op.h"
+#include "../../lib/srdb1/db_res.h"
+#include "../../lib/srdb1/db_con.h"
+
+int km_bdb_val2str(db_val_t* _v, char* _s, int* _len);
+int bdb_str2val(db_type_t _t, db_val_t* _v, char* _s, int _l);
+
+#endif
+

+ 1255 - 0
modules/db_berkeley/km_db_berkeley.c

@@ -0,0 +1,1255 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+#include "../../str.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+
+#include "../../sr_module.h"
+#include "../../lib/srdb1/db_res.h"
+#include "../../lib/srdb1/db.h"
+#include "km_db_berkeley.h"
+#include "km_bdb_lib.h"
+#include "km_bdb_res.h"
+#include "km_bdb_mi.h"
+#include "bdb_mod.h"
+
+#ifndef CFG_DIR
+#define CFG_DIR "/tmp"
+#endif
+
+#define BDB_ID		"berkeley://"
+#define BDB_ID_LEN	(sizeof(BDB_ID)-1)
+#define BDB_PATH_LEN	256
+
+#define BDB_KEY   1
+#define BDB_VALUE 0
+
+/*MODULE_VERSION*/
+
+int bdb_bind_api(db_func_t *dbb);
+
+/*
+ * Exported functions
+ */
+static kam_cmd_export_t cmds[] = {
+	{"db_bind_api",    (cmd_function)bdb_bind_api,   0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}
+};
+
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"auto_reload",        INT_PARAM, &auto_reload },
+	{"log_enable",         INT_PARAM, &log_enable  },
+	{"journal_roll_interval", INT_PARAM, &journal_roll_interval  },
+	{0, 0, 0}
+};
+
+/*
+ * Exported MI functions
+ */
+static mi_export_t mi_cmds[] = {
+	{ MI_BDB_RELOAD, mi_bdb_reload, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0}
+};
+
+struct kam_module_exports kam_exports = {	
+	"db_berkeley",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,     /* Exported functions */
+	params,   /* Exported parameters */
+	0,        /* exported statistics */
+	mi_cmds,  /* exported MI functions */
+	0,        /* exported pseudo-variables */
+	0,        /* extra processes */
+	km_mod_init, /* module initialization function */
+	0,        /* response function*/
+	km_destroy,  /* destroy function */
+	0         /* per-child init function */
+};
+
+int km_mod_init(void)
+{
+	db_parms_t p;
+	
+	p.auto_reload = auto_reload;
+	p.log_enable = log_enable;
+	p.cache_size  = (4 * 1024 * 1024); //4Mb
+	p.journal_roll_interval = journal_roll_interval;
+	
+	if(km_bdblib_init(&p))
+		return -1;
+
+	return 0;
+}
+
+void km_destroy(void)
+{
+	km_bdblib_destroy();
+}
+
+int bdb_bind_api(db_func_t *dbb)
+{
+	if(dbb==NULL)
+		return -1;
+
+	memset(dbb, 0, sizeof(db_func_t));
+
+	dbb->use_table   = bdb_use_table;
+	dbb->init        = bdb_init;
+	dbb->close       = bdb_close;
+	dbb->query       = (db_query_f)km_bdb_query;
+	dbb->free_result = bdb_free_query;
+	dbb->insert      = (db_insert_f)bdb_insert;
+	dbb->delete      = (db_delete_f)bdb_delete; 
+	dbb->update      = (db_update_f)bdb_update;
+
+	return 0;
+}
+
+int bdb_use_table(db1_con_t* _h, const str* _t)
+{
+	return db_use_table(_h, _t);
+}
+
+/*
+ * Initialize database connection
+ */
+db1_con_t* bdb_init(const str* _sqlurl)
+{
+	db1_con_t* _res;
+	str _s;
+	char bdb_path[BDB_PATH_LEN];
+	
+	if (!_sqlurl || !_sqlurl->s) {
+		LM_ERR("invalid parameter value\n");
+		return 0;
+	}
+	
+	_s.s = _sqlurl->s;
+	_s.len = _sqlurl->len;
+	if(_s.len <= BDB_ID_LEN || strncmp(_s.s, BDB_ID, BDB_ID_LEN)!=0)
+	{
+		LM_ERR("invalid database URL - should be:"
+			" <%s[/]path/to/directory>\n", BDB_ID);
+		return NULL;
+	}
+	_s.s   += BDB_ID_LEN;
+	_s.len -= BDB_ID_LEN;
+	
+	if(_s.s[0]!='/')
+	{
+		if(sizeof(CFG_DIR)+_s.len+2 > BDB_PATH_LEN)
+		{
+			LM_ERR("path to database is too long\n");
+			return NULL;
+		}
+		strcpy(bdb_path, CFG_DIR);
+		bdb_path[sizeof(CFG_DIR)] = '/';
+		strncpy(&bdb_path[sizeof(CFG_DIR)+1], _s.s, _s.len);
+		_s.len += sizeof(CFG_DIR);
+		_s.s = bdb_path;
+	}
+	
+	_res = pkg_malloc(sizeof(db1_con_t)+sizeof(bdb_con_t));
+	if (!_res)
+	{
+		LM_ERR("No private memory left\n");
+		return NULL;
+	}
+	memset(_res, 0, sizeof(db1_con_t) + sizeof(bdb_con_t));
+	_res->tail = (unsigned long)((char*)_res+sizeof(db1_con_t));
+
+	LM_INFO("using database at: %.*s", _s.len, _s.s);
+	BDB_CON_CONNECTION(_res) = km_bdblib_get_db(&_s);
+	if (!BDB_CON_CONNECTION(_res))
+	{
+		LM_ERR("cannot get the link to database\n");
+		return NULL;
+	}
+
+    return _res;
+}
+
+
+/*
+ * Close a database connection
+ */
+void bdb_close(db1_con_t* _h)
+{
+	if(BDB_CON_RESULT(_h))
+		db_free_result(BDB_CON_RESULT(_h));
+	pkg_free(_h);
+}
+
+/* 
+ * n can be the dbenv path or a table name
+*/
+int bdb_reload(char* _n)
+{
+	int rc = 0;
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("[bdb_reload] Initiate RELOAD in %s\n", _n);
+#endif
+
+	if ((rc = km_bdblib_close(_n)) != 0) 
+	{	LM_ERR("[bdb_reload] Error while closing db_berkeley DB.\n");
+		return rc;
+	}
+
+	if ((rc = km_bdblib_reopen(_n)) != 0) 
+	{	LM_ERR("[bdb_reload] Error while reopening db_berkeley DB.\n");
+		return rc;
+	}
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("[bdb_reload] RELOAD successful in %s\n", _n);
+#endif
+
+	return rc;
+}
+
+/*
+ * Attempts to reload a Berkeley database; reloads when the inode changes
+ */
+void bdb_check_reload(db1_con_t* _con)
+{
+	str s;
+	char* p;
+	int rc, len;
+	struct stat st;
+	database_p db;
+	char n[MAX_ROW_SIZE];
+	char t[MAX_TABLENAME_SIZE];
+	table_p tp = NULL;
+	tbl_cache_p tbc = NULL;
+	
+	p=n;
+	rc = len = 0;
+	
+	/*get dbenv name*/
+	db = BDB_CON_CONNECTION(_con);
+	if(!db->dbenv)	return;
+	s.s = db->name.s;
+	s.len = db->name.len;
+	len+=s.len;
+	
+	if(len > MAX_ROW_SIZE)
+	{	LM_ERR("dbenv name too long \n");
+		return;
+	}
+	
+	strncpy(p, s.s, s.len);
+	p+=s.len;
+	
+	len++;
+	if(len > MAX_ROW_SIZE)
+	{	LM_ERR("dbenv name too long \n");
+		return;
+	}
+	
+	/*append slash */
+	*p = '/';
+	p++;
+	
+	/*get table name*/
+	s.s = CON_TABLE(_con)->s;
+	s.len = CON_TABLE(_con)->len;
+	len+=s.len;
+	
+	if((len>MAX_ROW_SIZE) || (s.len > MAX_TABLENAME_SIZE) )
+	{	LM_ERR("table name too long \n");
+		return;
+	}
+
+	strncpy(t, s.s, s.len);
+	t[s.len] = 0;
+	
+	strncpy(p, s.s, s.len);
+	p+=s.len;
+	*p=0;
+	
+	if( (tbc = km_bdblib_get_table(db, &s)) == NULL)
+		return;
+	
+	if( (tp = tbc->dtp) == NULL)
+		return;
+	
+	LM_DBG("stat file [%.*s]\n", len, n);
+	rc = stat(n, &st);
+	if(!rc)
+	{	if((tp->ino!=0) && (st.st_ino != tp->ino))
+			bdb_reload(t); /*file changed on disk*/
+		
+		tp->ino = st.st_ino;
+	}
+
+}
+
+
+/*
+ * Free all memory allocated by get_result
+ */
+int bdb_free_query(db1_con_t* _h, db1_res_t* _r)
+{
+	if(_r)
+		db_free_result(_r);
+	if(_h)
+		BDB_CON_RESULT(_h) = NULL;
+	return 0;
+}
+
+
+/*
+ * Query table for specified rows
+ * _con: structure representing database connection
+ * _k: key names
+ * _op: operators
+ * _v: values of the keys that must match
+ * _c: column names to return
+ * _n: number of key=values pairs to compare
+ * _nc: number of columns to return
+ * _o: order by the specified column
+ */
+int km_bdb_query(db1_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v, 
+			db_key_t* _c, int _n, int _nc, db_key_t _o, db1_res_t** _r)
+{
+	tbl_cache_p _tbc = NULL;
+	table_p _tp = NULL;
+	char kbuf[MAX_ROW_SIZE];
+	char dbuf[MAX_ROW_SIZE];
+	u_int32_t i, len, ret; 
+	int klen=MAX_ROW_SIZE;
+	int *lkey=NULL, *lres=NULL;
+	DBT key, data;
+	DB *db;
+	DBC *dbcp;
+
+	if ((!_con) || (!_r) || !CON_TABLE(_con))
+	{
+#ifdef BDB_EXTRA_DEBUG
+		LM_ERR("Invalid parameter value\n");
+#endif
+		return -1;
+	}
+	*_r = NULL;
+	
+	/*check if underlying DB file has changed inode */
+	if(auto_reload)
+		bdb_check_reload(_con);
+
+	_tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_con), (str*)CON_TABLE(_con));
+	if(!_tbc)
+	{	LM_WARN("table does not exist!\n");
+		return -1;
+	}
+
+	_tp = _tbc->dtp;
+	if(!_tp)
+	{	LM_WARN("table not loaded!\n");
+		return -1;
+	}
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("QUERY in %.*s\n", _tp->name.len, _tp->name.s);
+
+	if (_o)  LM_DBG("DONT-CARE : _o: order by the specified column \n");
+	if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n");
+#endif
+	
+	db = _tp->db;
+	if(!db) return -1;
+	
+	memset(&key, 0, sizeof(DBT));
+	memset(kbuf, 0, MAX_ROW_SIZE);
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+
+	/* if _c is NULL and _nc is zero, you will get all table 
+	   columns in the result
+	*/
+	if (_c)
+	{	lres = bdb_get_colmap(_tbc->dtp, _c, _nc);
+		if(!lres)
+		{	ret = -1;
+			goto error;
+		}
+	}
+	
+	if(_k)
+	{	lkey = bdb_get_colmap(_tbc->dtp, _k, _n);
+		if(!lkey) 
+		{	ret = -1;
+			goto error;
+		}
+	}
+	else
+	{
+		DB_HASH_STAT st;
+		memset(&st, 0, sizeof(DB_HASH_STAT));
+		i =0 ;
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("SELECT * FROM %.*s\n", _tp->name.len, _tp->name.s);
+#endif
+
+		/* Acquire a cursor for the database. */
+		if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) 
+		{	LM_ERR("Error creating cursor\n");
+			goto error;
+		}
+		
+		/*count the number of records*/
+		while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0)
+		{	if(!strncasecmp((char*)key.data,"METADATA",8)) 
+				continue;
+			i++;
+		}
+		
+		dbcp->c_close(dbcp);
+		ret=0;
+		
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("%i = SELECT COUNT(*) FROM %.*s\n", i, _tp->name.len, _tp->name.s);
+#endif
+
+		*_r = db_new_result();
+		if (!*_r) 
+		{	LM_ERR("no memory left for result \n");
+			ret = -2;
+			goto error;
+		}
+		
+		if(i == 0)
+		{	
+			/*return empty table*/
+			RES_ROW_N(*_r) = 0;
+			BDB_CON_RESULT(_con) = *_r;
+			return 0;
+		}
+		
+		/*allocate N rows in the result*/
+		RES_ROW_N(*_r) = i;
+		len  = sizeof(db_row_t) * i;
+		RES_ROWS(*_r) = (db_row_t*)pkg_malloc( len );
+		memset(RES_ROWS(*_r), 0, len);
+		
+		/*fill in the column part of db1_res_t (metadata) */
+		if ((ret = bdb_get_columns(_tbc->dtp, *_r, lres, _nc)) < 0) 
+		{	LM_ERR("Error while getting column names\n");
+			goto error;
+		}
+		
+		/* Acquire a cursor for the database. */
+		if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) 
+		{	LM_ERR("Error creating cursor\n");
+			goto error;
+		}
+
+		/*convert each record into a row in the result*/
+		i =0 ;
+		while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0)
+		{
+			if(!strncasecmp((char*)key.data,"METADATA",8)) 
+				continue;
+			
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("KEY: [%.*s]\nDATA: [%.*s]\n"
+			, (int)   key.size
+			, (char *)key.data
+			, (int)   data.size
+			, (char *)data.data);
+#endif
+
+			/*fill in the row part of db1_res_t */
+			if ((ret=bdb_append_row( *_r, dbuf, lres, i)) < 0) 
+			{	LM_ERR("Error while converting row\n");
+				goto error;
+			}
+			i++;
+		}
+		
+		dbcp->c_close(dbcp);
+		BDB_CON_RESULT(_con) = *_r;
+		return 0; 
+	}
+
+	if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) 
+	{	LM_ERR("error in query key \n");
+		goto error;
+	}
+
+	key.data = kbuf;
+	key.ulen = MAX_ROW_SIZE;
+	key.flags = DB_DBT_USERMEM;
+	key.size = klen;
+
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+
+	/*create an empty db1_res_t which gets returned even if no result*/
+	*_r = db_new_result();
+	if (!*_r) 
+	{	LM_ERR("no memory left for result \n");
+		ret = -2;
+		goto error;
+	}
+	RES_ROW_N(*_r) = 0;
+	BDB_CON_RESULT(_con) = *_r;
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("SELECT  KEY: [%.*s]\n"
+			, (int)   key.size
+			, (char *)key.data );
+#endif
+
+	/*query Berkely DB*/
+	if ((ret = db->get(db, NULL, &key, &data, 0)) == 0) 
+	{
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("RESULT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
+			, (int)   key.size
+			, (char *)key.data
+			, (int)   data.size
+			, (char *)data.data);
+#endif
+
+		/*fill in the col part of db1_res_t */
+		if ((ret = bdb_get_columns(_tbc->dtp, *_r, lres, _nc)) < 0) 
+		{	LM_ERR("Error while getting column names\n");
+			goto error;
+		}
+		/*fill in the row part of db1_res_t */
+		if ((ret=bdb_convert_row( *_r, dbuf, lres)) < 0) 
+		{	LM_ERR("Error while converting row\n");
+			goto error;
+		}
+		
+	}
+	else
+	{	
+		/*Berkeley DB error handler*/
+		switch(ret)
+		{
+		
+		case DB_NOTFOUND:
+		
+#ifdef BDB_EXTRA_DEBUG
+			LM_DBG("NO RESULT for QUERY \n");
+#endif
+		
+			ret=0;
+			break;
+		/*The following are all critical/fatal */
+		case DB_LOCK_DEADLOCK:	
+		// The operation was selected to resolve a deadlock. 
+		case DB_SECONDARY_BAD:
+		// A secondary index references a nonexistent primary key. 
+		case DB_RUNRECOVERY:
+		default:
+			LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
+			km_bdblib_recover(_tp,ret);
+			goto error;
+		}
+	}
+	
+	if(lkey)
+		pkg_free(lkey);
+	if(lres)
+		pkg_free(lres);
+	
+	return ret;
+	
+error:
+	if(lkey)
+		pkg_free(lkey);
+	if(lres)
+		pkg_free(lres);
+	if(*_r) 
+		db_free_result(*_r);
+	*_r = NULL;
+	
+	return ret;
+}
+
+
+
+/*
+ * Raw SQL query
+ */
+int bdb_raw_query(db1_con_t* _h, char* _s, db1_res_t** _r)
+{
+	LM_CRIT("DB RAW QUERY not implemented!\n");
+	return -1;
+}
+
+/*
+ * Insert a row into table
+ */
+int bdb_insert(db1_con_t* _h, db_key_t* _k, db_val_t* _v, int _n)
+{
+	tbl_cache_p _tbc = NULL;
+	table_p _tp = NULL;
+	char kbuf[MAX_ROW_SIZE];
+	char dbuf[MAX_ROW_SIZE];
+	int i, j, ret, klen, dlen;
+	int *lkey=NULL;
+	DBT key, data;
+	DB *db;
+
+	i = j = ret = 0;
+	klen=MAX_ROW_SIZE;
+	dlen=MAX_ROW_SIZE;
+
+	if ((!_h) || (!_v) || !CON_TABLE(_h))
+	{	return -1;
+	}
+
+	if (!_k)
+	{
+#ifdef BDB_EXTRA_DEBUG
+	LM_ERR("DB INSERT without KEYs not implemented! \n");
+#endif
+		return -2;
+	}
+
+	_tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h));
+	if(!_tbc)
+	{	LM_WARN("table does not exist!\n");
+		return -3;
+	}
+
+	_tp = _tbc->dtp;
+	if(!_tp)
+	{	LM_WARN("table not loaded!\n");
+		return -4;
+	}
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("INSERT in %.*s\n", _tp->name.len, _tp->name.s );
+#endif
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(kbuf, 0, klen);
+	
+	if(_tp->ncols<_n) 
+	{	LM_WARN("more values than columns!!\n");
+		return -5;
+	}
+
+	lkey = bdb_get_colmap(_tp, _k, _n);
+	if(!lkey)  return -7;
+
+	/* verify col types provided */
+	for(i=0; i<_n; i++)
+	{	j = (lkey)?lkey[i]:i;
+		if(bdb_is_neq_type(_tp->colp[j]->type, _v[i].type))
+		{
+			LM_WARN("incompatible types v[%d] - c[%d]!\n", i, j);
+			ret = -8;
+			goto error;
+		}
+	}
+	
+	/* make the key */
+	if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) 
+	{	LM_ERR("Error in km_bdblib_valtochar  \n");
+		ret = -9;
+		goto error;
+	}
+	
+	key.data = kbuf;
+	key.ulen = MAX_ROW_SIZE;
+	key.flags = DB_DBT_USERMEM;
+	key.size = klen;
+
+	//make the value (row)
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+
+	if ( (ret = km_bdblib_valtochar(_tp, lkey, dbuf, &dlen, _v, _n, BDB_VALUE)) != 0 ) 
+	{	LM_ERR("Error in km_bdblib_valtochar \n");
+		ret = -9;
+		goto error;
+	}
+
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	data.size = dlen;
+
+	if ((ret = db->put(db, NULL, &key, &data, 0)) == 0) 
+	{
+		km_bdblib_log(JLOG_INSERT, _tp, dbuf, dlen);
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("INSERT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
+		, (int)   key.size
+		, (char *)key.data
+		, (int)   data.size
+		, (char *)data.data);
+#endif
+	}
+	else
+	{	/*Berkeley DB error handler*/
+		switch(ret)
+		{
+		/*The following are all critical/fatal */
+		case DB_LOCK_DEADLOCK:	
+		/* The operation was selected to resolve a deadlock. */ 
+		
+		case DB_RUNRECOVERY:
+		default:
+			LM_CRIT("DB->put error: %s.\n", db_strerror(ret));
+			km_bdblib_recover(_tp, ret);
+			goto error;
+		}
+	}
+	
+error:
+	if(lkey)
+		pkg_free(lkey);
+	
+	return ret;
+
+}
+
+/*
+ * Delete a row from table
+ *
+ * To delete ALL rows:
+ *   do Not specify any keys, or values, and _n <=0
+ *
+ */
+int bdb_delete(db1_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n)
+{
+	tbl_cache_p _tbc = NULL;
+	table_p _tp = NULL;
+	char kbuf[MAX_ROW_SIZE];
+	int i, j, ret, klen;
+	int *lkey=NULL;
+	DBT key;
+	DB *db;
+	DBC *dbcp;
+
+	i = j = ret = 0;
+	klen=MAX_ROW_SIZE;
+
+	if (_op)
+		return ( _bdb_delete_cursor(_h, _k, _op, _v, _n) );
+
+	if ((!_h) || !CON_TABLE(_h))
+		return -1;
+
+	_tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h));
+	if(!_tbc)
+	{	LM_WARN("table does not exist!\n");
+		return -3;
+	}
+
+	_tp = _tbc->dtp;
+	if(!_tp)
+	{	LM_WARN("table not loaded!\n");
+		return -4;
+	}
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("DELETE in %.*s\n", _tp->name.len, _tp->name.s );
+#endif
+
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(kbuf, 0, klen);
+
+	if(!_k || !_v || _n<=0)
+	{
+		/* Acquire a cursor for the database. */
+		if ((ret = db->cursor(db, NULL, &dbcp, DB_WRITECURSOR) ) != 0) 
+		{	LM_ERR("Error creating cursor\n");
+			goto error;
+		}
+		
+		while ((ret = dbcp->c_get(dbcp, &key, NULL, DB_NEXT)) == 0)
+		{
+			if(!strncasecmp((char*)key.data,"METADATA",8)) 
+				continue;
+#ifdef BDB_EXTRA_DEBUG
+			LM_DBG("KEY: [%.*s]\n"
+				, (int)   key.size
+				, (char *)key.data);
+#endif
+			ret = dbcp->c_del(dbcp, 0);
+		}
+		
+		dbcp->c_close(dbcp);
+		return 0;
+	}
+
+	lkey = bdb_get_colmap(_tp, _k, _n);
+	if(!lkey)  return -5;
+
+	/* make the key */
+	if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &klen, _v, _n, BDB_KEY)) != 0 ) 
+	{	LM_ERR("Error in bdblib_makekey\n");
+		ret = -6;
+		goto error;
+	}
+
+	key.data = kbuf;
+	key.ulen = MAX_ROW_SIZE;
+	key.flags = DB_DBT_USERMEM;
+	key.size = klen;
+
+	if ((ret = db->del(db, NULL, &key, 0)) == 0)
+	{
+		km_bdblib_log(JLOG_DELETE, _tp, kbuf, klen);
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("DELETED ROW \n KEY: %s \n", (char *)key.data);
+#endif
+	}
+	else
+	{	/*Berkeley DB error handler*/
+		switch(ret){
+			
+		case DB_NOTFOUND:
+			ret = 0;
+			break;
+			
+		/*The following are all critical/fatal */
+		case DB_LOCK_DEADLOCK:	
+		/* The operation was selected to resolve a deadlock. */ 
+		case DB_SECONDARY_BAD:
+		/* A secondary index references a nonexistent primary key. */
+		case DB_RUNRECOVERY:
+		default:
+			LM_CRIT("DB->del error: %s.\n"
+				, db_strerror(ret));
+			km_bdblib_recover(_tp, ret);
+			goto error;
+		}
+	}
+
+	ret = 0;
+	
+error:
+	if(lkey)
+		pkg_free(lkey);
+	
+	return ret;
+
+}
+
+/*
+_bdb_delete_cursor -- called from bdb_delete when the query involves operators 
+  other than equal '='. Adds support for queries like this:
+	DELETE from SomeTable WHERE _k[0] < _v[0]
+  In this case, the keys _k are not the actually schema keys, so we need to 
+  iterate via cursor to perform this operation.
+*/
+int _bdb_delete_cursor(db1_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n)
+{
+	tbl_cache_p _tbc = NULL;
+	table_p _tp = NULL;
+	db1_res_t* _r   = NULL;
+	char kbuf[MAX_ROW_SIZE];
+	char dbuf[MAX_ROW_SIZE];
+	int i, ret, klen=MAX_ROW_SIZE;
+	DBT key, data;
+	DB *db;
+	DBC *dbcp;
+	int *lkey=NULL;
+	
+	i = ret = 0;
+	
+	if ((!_h) || !CON_TABLE(_h))
+		return -1;
+
+	_tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_h), (str*)CON_TABLE(_h));
+	if(!_tbc)
+	{	LM_WARN("table does not exist!\n");
+		return -3;
+	}
+
+	_tp = _tbc->dtp;
+	if(!_tp)
+	{	LM_WARN("table not loaded!\n");
+		return -4;
+	}
+	
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("DELETE by cursor in %.*s\n", _tp->name.len, _tp->name.s );
+#endif
+
+	if(_k)
+	{	lkey = bdb_get_colmap(_tp, _k, _n);
+		if(!lkey) 
+		{	ret = -1;
+			goto error;
+		}
+	}
+	
+	/* create an empty db1_res_t which gets returned even if no result */
+	_r = db_new_result();
+	if (!_r) 
+	{	LM_ERR("no memory for result \n");
+	}
+	
+	RES_ROW_N(_r) = 0;
+	
+	/* fill in the col part of db1_res_t */
+	if ((ret = bdb_get_columns(_tp, _r, 0, 0)) != 0) 
+	{	LM_ERR("Error while getting column names\n");
+		goto error;
+	}
+	
+	db = _tp->db;
+	memset(&key, 0, sizeof(DBT));
+	memset(kbuf, 0, klen);
+	memset(&data, 0, sizeof(DBT));
+	memset(dbuf, 0, MAX_ROW_SIZE);
+	
+	data.data = dbuf;
+	data.ulen = MAX_ROW_SIZE;
+	data.flags = DB_DBT_USERMEM;
+	
+	/* Acquire a cursor for the database. */
+	if ((ret = db->cursor(db, NULL, &dbcp, DB_WRITECURSOR)) != 0) 
+	{	LM_ERR("Error creating cursor\n");
+	}
+	
+	while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0)
+	{
+		if(!strncasecmp((char*)key.data,"METADATA",8))
+			continue;
+		
+		/*fill in the row part of db1_res_t */
+		if ((ret=bdb_convert_row( _r, dbuf, 0)) < 0) 
+		{	LM_ERR("Error while converting row\n");
+			goto error;
+		}
+		
+		if(bdb_row_match(_k, _op, _v, _n, _r, lkey ))
+		{
+
+#ifdef BDB_EXTRA_DEBUG
+			LM_DBG("DELETE ROW by KEY:  [%.*s]\n", (int) key.size, 
+				(char *)key.data);
+#endif
+
+			if((ret = dbcp->c_del(dbcp, 0)) != 0)
+			{	
+				/* Berkeley DB error handler */
+				LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
+				km_bdblib_recover(_tp,ret);
+			}
+			
+		}
+		
+		memset(dbuf, 0, MAX_ROW_SIZE);
+		db_free_rows( _r);
+	}
+	ret = 0;
+	
+error:
+	if(dbcp)
+		dbcp->c_close(dbcp);
+	if(_r)
+		db_free_result(_r);
+	if(lkey)
+		pkg_free(lkey);
+	
+	return ret;
+}
+
+/*
+ * Updates a row in table
+ * Limitation: only knows how to update a single row
+ *
+ * _con: structure representing database connection
+ * _k: key names
+ * _op: operators
+ * _v: values of the keys that must match
+ * _uk: update keys; cols that need to be updated 
+ * _uv: update values; col values that need to be commited
+ * _un: number of rows to update
+ */
+int bdb_update(db1_con_t* _con, db_key_t* _k, db_op_t* _op, db_val_t* _v,
+	      db_key_t* _uk, db_val_t* _uv, int _n, int _un)
+{
+	char *c, *t;
+	int ret, i, qcol, len, sum;
+	int *lkey=NULL;
+	tbl_cache_p _tbc = NULL;
+	table_p _tp = NULL;
+	char kbuf[MAX_ROW_SIZE];
+	char qbuf[MAX_ROW_SIZE];
+	char ubuf[MAX_ROW_SIZE];
+	DBT key, qdata, udata;
+	DB *db;
+	
+	sum = ret = i = qcol = len = 0;
+	
+	if (!_con || !CON_TABLE(_con) || !_uk || !_uv || _un <= 0)
+		return -1;
+
+	_tbc = km_bdblib_get_table(BDB_CON_CONNECTION(_con), (str*)CON_TABLE(_con));
+	if(!_tbc)
+	{	LM_ERR("table does not exist\n");
+		return -1;
+	}
+
+	_tp = _tbc->dtp;
+	if(!_tp)
+	{	LM_ERR("table not loaded\n");
+		return -1;
+	}
+	
+	db = _tp->db;
+	if(!db)
+	{	LM_ERR("DB null ptr\n");
+		return -1;
+	}
+	
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("UPDATE in %.*s\n", _tp->name.len, _tp->name.s);
+	if (_op) LM_DBG("DONT-CARE : _op: operators for refining query \n");
+#endif
+	
+	memset(&key, 0, sizeof(DBT));
+	memset(kbuf, 0, MAX_ROW_SIZE);
+	memset(&qdata, 0, sizeof(DBT));
+	memset(qbuf, 0, MAX_ROW_SIZE);
+	
+	qdata.data = qbuf;
+	qdata.ulen = MAX_ROW_SIZE;
+	qdata.flags = DB_DBT_USERMEM;
+	
+	if(_k)
+	{	lkey = bdb_get_colmap(_tbc->dtp, _k, _n);
+		if(!lkey) return -4;
+	}
+	else
+	{
+		LM_ERR("Null keys in update _k=0 \n");
+		return -1;
+	}
+	
+	len = MAX_ROW_SIZE;
+	
+	if ( (ret = km_bdblib_valtochar(_tp, lkey, kbuf, &len, _v, _n, BDB_KEY)) != 0 ) 
+	{	LM_ERR("Error in query key \n");
+		goto cleanup;
+	}
+	
+	if(lkey) pkg_free(lkey);
+	
+	key.data = kbuf;
+	key.ulen = MAX_ROW_SIZE;
+	key.flags = DB_DBT_USERMEM;
+	key.size = len;
+	
+	/*stage 1: QUERY Berkely DB*/
+	if ((ret = db->get(db, NULL, &key, &qdata, 0)) == 0) 
+	{
+
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("RESULT\nKEY:  [%.*s]\nDATA: [%.*s]\n"
+			, (int)   key.size
+			, (char *)key.data
+			, (int)   qdata.size
+			, (char *)qdata.data);
+#endif
+
+	}
+	else
+	{	goto db_error;
+	}
+	
+	/* stage 2: UPDATE row with new values */
+	
+	/* map the provided keys to those in our schema */ 
+	lkey = bdb_get_colmap(_tbc->dtp, _uk, _un);
+	if(!lkey) return -4;
+	
+	/* build a new row for update data (udata) */
+	memset(&udata, 0, sizeof(DBT));
+	memset(ubuf, 0, MAX_ROW_SIZE);
+	
+	/* loop over each column of the qbuf and copy it to our new ubuf unless
+	   its a field that needs to update
+	*/
+	c = strtok(qbuf, DELIM);
+	t = ubuf;
+	while( c!=NULL)
+	{	char* delim = DELIM;
+		int k;
+		
+		len = strlen(c);
+		sum+=len;
+		
+		if(sum > MAX_ROW_SIZE)
+		{	LM_ERR("value too long for string \n");
+			ret = -3;
+			goto cleanup;
+		}
+		
+		for(i=0;i<_un;i++)
+		{
+			k = lkey[i];
+			if (qcol == k)
+			{	/* update this col */
+				int j = MAX_ROW_SIZE - sum;
+				if( km_bdb_val2str( &_uv[i], t, &j) )
+				{	LM_ERR("value too long for string \n");
+					ret = -3;
+					goto cleanup;
+				}
+
+				goto next;
+			}
+			
+		}
+		
+		/* copy original column to the new column */
+		strncpy(t, c, len);
+
+next:
+		t+=len;
+		
+		/* append DELIM */
+		sum += DELIM_LEN;
+		if(sum > MAX_ROW_SIZE)
+		{	LM_ERR("value too long for string \n");
+			ret = -3;
+			goto cleanup;
+		}
+		
+		strncpy(t, delim, DELIM_LEN);
+		t += DELIM_LEN;
+		
+		c = strtok(NULL, DELIM);
+		qcol++;
+	}
+	
+	ubuf[sum]  = '0';
+	udata.data = ubuf;
+	udata.ulen  = MAX_ROW_SIZE;
+	udata.flags = DB_DBT_USERMEM;
+	udata.size  = sum;
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("MODIFIED Data\nKEY:  [%.*s]\nDATA: [%.*s]\n"
+		, (int)   key.size
+		, (char *)key.data
+		, (int)   udata.size
+		, (char *)udata.data);
+#endif
+	/* stage 3: DELETE old row using key*/
+	if ((ret = db->del(db, NULL, &key, 0)) == 0)
+	{
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("DELETED ROW\nKEY: %s \n", (char *)key.data);
+#endif
+	}
+	else
+	{	goto db_error;
+	}
+	
+	/* stage 4: INSERT new row with key*/
+	if ((ret = db->put(db, NULL, &key, &udata, 0)) == 0) 
+	{
+		km_bdblib_log(JLOG_UPDATE, _tp, ubuf, sum);
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("INSERT \nKEY:  [%.*s]\nDATA: [%.*s]\n"
+		, (int)   key.size
+		, (char *)key.data
+		, (int)   udata.size
+		, (char *)udata.data);
+#endif
+	}
+	else
+	{	goto db_error;
+	}
+
+#ifdef BDB_EXTRA_DEBUG
+	LM_DBG("UPDATE COMPLETE \n");
+#endif
+
+
+cleanup:
+	if(lkey)
+		pkg_free(lkey);
+	
+	return ret;
+
+
+db_error:
+
+	/*Berkeley DB error handler*/
+	switch(ret)
+	{
+	
+	case DB_NOTFOUND:
+	
+#ifdef BDB_EXTRA_DEBUG
+		LM_DBG("NO RESULT \n");
+#endif
+		return -1;
+	
+	/* The following are all critical/fatal */
+	case DB_LOCK_DEADLOCK:	
+	/* The operation was selected to resolve a deadlock. */
+	case DB_SECONDARY_BAD:
+	/* A secondary index references a nonexistent primary key.*/ 
+	case DB_RUNRECOVERY:
+	default:
+		LM_CRIT("DB->get error: %s.\n", db_strerror(ret));
+		km_bdblib_recover(_tp,ret);
+	}
+	
+	if(lkey)
+		pkg_free(lkey);
+	
+	return ret;
+}

+ 102 - 0
modules/db_berkeley/km_db_berkeley.h

@@ -0,0 +1,102 @@
+/*
+ * $Id$
+ *
+ * db_berkeley module, portions of this code were templated using
+ * the dbtext and postgres modules.
+
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * History:
+ * --------
+ * 2007-09-19  genesis (wiquan)
+ */
+
+
+#ifndef _KM_BDB_H_
+#define _KM_BDB_H_
+
+#include "../../lib/srdb1/db_con.h"
+#include "../../lib/srdb1/db_res.h"
+#include "../../lib/srdb1/db_key.h"
+#include "../../lib/srdb1/db_op.h"
+#include "../../lib/srdb1/db_val.h"
+#include "../../lib/srdb1/db.h"
+
+/* reloads the berkeley db */
+int bdb_reload(char* _n);
+
+void bdb_check_reload(db1_con_t* _con);
+int  bdb_use_table(db1_con_t* _h, const str* _t);
+
+/*
+ * Initialize database connection
+ */
+db1_con_t* bdb_init(const str* _sqlurl);
+
+
+/*
+ * Close a database connection
+ */
+void bdb_close(db1_con_t* _h);
+
+
+/*
+ * Free all memory allocated by get_result
+ */
+int bdb_free_query(db1_con_t* _h, db1_res_t* _r);
+
+
+/*
+ * Do a query
+ */
+int km_bdb_query(db1_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, 
+				 db_key_t* _c, int _n, int _nc, db_key_t _o, db1_res_t** _r);
+
+
+/*
+ * Raw SQL query
+ */
+int bdb_raw_query(db1_con_t* _h, char* _s, db1_res_t** _r);
+
+
+/*
+ * Insert a row into table
+ */
+int bdb_insert(db1_con_t* _h, db_key_t* _k, db_val_t* _v, int _n);
+
+
+/*
+ * Delete a row from table
+ */
+int bdb_delete(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+int _bdb_delete_cursor(db1_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n);
+
+/*
+ * Update a row in table
+ */
+int bdb_update(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
+	      db_key_t* _uk, db_val_t* _uv, int _n, int _un);
+
+int bdb_bind_api(db_func_t *dbb);
+
+int km_mod_init(void);
+void km_destroy(void);
+
+#endif
+

+ 55 - 0
modules/db_berkeley/km_doc/db_berkeley.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+
+<!ENTITY admin SYSTEM "db_berkeley_admin.xml">
+<!ENTITY faq SYSTEM "../../../doc/module_faq.xml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.xml">
+%docentities;
+
+]>
+
+<book>
+    <bookinfo>
+	<title>Berkeley DB Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Will</firstname>
+		<surname>Quan</surname>
+		<affiliation><orgname>Cisco Systems</orgname></affiliation>
+		<address>
+		<email>[email protected]</email>
+		<otheraddr>
+		<ulink url="http://www.cisco.com">http://www.cisco.com</ulink>
+		</otheraddr>
+		</address>
+	    </author>
+	    <editor>
+		<firstname>Will</firstname>
+		<surname>Quan</surname>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2007</year>
+	    <holder>Cisco Systems</holder>
+	</copyright>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision: 846 $</revnumber>
+		<date>$Date: 2006-05-22 09:15:40 -0500 (Mon, 22 May 2006) $</date>
+	    </revision>
+	</revhistory>
+    </bookinfo>
+    <toc></toc>
+    
+    &admin;
+    &faq;
+    
+</book>

+ 582 - 0
modules/db_berkeley/km_doc/db_berkeley_admin.xml

@@ -0,0 +1,582 @@
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This is a module which integrates the Berkeley DB into Kamailio.
+		It implements the DB API defined in Kamailio.
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>No dependencies on other &kamailio; modules</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>Berkeley Berkeley DB 4.6</emphasis> - an embedded database.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>auto_reload</varname> (integer)</title>
+		<para>
+		The auto-reload will close and reopen a Berkeley DB when the
+		files inode has changed. The operation occurs only duing a query. 
+		Other operations such as insert or delete, do not invoke auto_reload.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (1 - on / 0 - off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>auto_reload</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_berkeley", "auto_reload", 1)
+...
+		</programlisting>
+		</example>
+	</section>
+	
+	<section>
+		<title><varname>log_enable</varname> (integer)</title>
+		<para>
+		The log_enable boolean controls when to create journal files.
+		The following operations can be journaled: 
+		INSERT, UPDATE, DELETE. Other operations such as SELECT, do not. 
+		This journaling are required if you need to recover from a corrupt 
+		DB file. That is, kambdb_recover requires these to rebuild 
+		the db file. If you find this log feature useful, you may 
+		also be interested in the METADATA_LOGFLAGS bitfield that each 
+		table has. It will allow you to control which operations to 
+		journal, and the destination (like syslog, stdout, local-file). 
+		Refer to bdblib_log()  and documentation on METADATA.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (1 - on / 0 - off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>log_enable</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_berkeley", "log_enable", 1)
+...
+		</programlisting>
+		</example>
+	</section>
+	
+	<section>
+		<title><varname>journal_roll_interval</varname> (integer seconds)</title>
+		<para>
+		The journal_roll_interval will close and open a new log file. 
+		The roll operation occurs only at the end of writing a log, 
+		so it is not guaranteed to to roll 'on time'.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (off).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>journal_roll_interval</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("db_berkeley", "journal_roll_interval", 3600)
+...
+		</programlisting>
+		</example>
+	</section>
+	</section>
+	
+	<section>
+	<title>Exported Functions</title>
+		<para>
+		No function exported to be used from configuration file.
+		</para>
+	</section>
+	
+	<section>
+	<title>Exported MI Functions</title>
+	<section>
+		<title><function moreinfo="none">bdb_reload</function></title>
+		
+		<para>
+		Causes db_berkeley module to re-read the contents of specified table (or dbenv).
+		The db_berkeley DB actually loads each table on demand, as opposed to loading all
+		at mod_init time. The bdb_reload operation is implemented as a close followed by a reopen.
+		Note- bdb_reload will fail if a table has not been accessed before (because the close 
+		will fail).
+		</para>
+		
+		<para>
+		Name: <emphasis>bdb_reload</emphasis>
+		</para>
+		
+		<para>Parameters: <emphasis>tablename (or db_path); to reload a particular table 
+		provide the tablename as the arguement (eg subscriber); to reload all tables provide the db_path to
+		the db files. The path can be found in kamctlrc DB_PATH variable. 
+		</emphasis></para>
+	</section>
+	</section>
+	
+	<section>
+	<title>Installation and Running</title>
+		<para>
+		First download, compile and install the Berkeley DB. This is 
+		outside the scope of this document. Documentation for this 
+		procedure is available on the Internet.
+		</para>
+		
+		<para>
+		Next, prepare to compile Kamailio with the db_berkeley module. 
+		In the directory /modules/db_berkeley, modify the Makefile to point 
+		to your distribution of Berkeley DB. You may also define 'BDB_EXTRA_DEBUG' 
+		to compile in extra debug logs. However, it is not a recommended 
+		deployment to production servers.
+		</para>
+		
+		<para>
+		Because the module dependes on an external library, the db_berkeley module is not
+		compiled and installed by default. You can use one of the next options.
+		</para>
+		
+		<itemizedlist>
+			<listitem>
+			<para>
+			edit the "Makefile" and remove "db_berkeley" from "excluded_modules"
+			list. Then follow the standard procedure to install &kamailio;:
+			"make all; make install".
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			from command line use: 'make all include_modules="db_berkeley";
+			make install include_modules="db_berkeley"'.
+			</para>
+			</listitem>
+		</itemizedlist>
+		
+		<para>
+		Installation of Kamailio is performed by simply running make install
+		as root user of the main directory. This will install the binaries 
+		in /usr/local/sbin/.
+		If this was successful, openser control engine files should now 
+		be installed as /usr/local/sbin/kamdbctl.
+		</para>
+		
+		<para>
+		Decide where (on the filesystem) you want to install the Berkeley DB files.
+		For instance, '/usr/local/etc/kamailio/db_berkeley' directory.
+		Make note of this directory as we need to add this path to the kamctlrc file.
+		Note: Kamailio will not startup without these DB files.
+		</para>
+		
+		<para>
+		Edit kamctlrc - There are two parameters in this file that should be 
+		configured before openserctrdb script can work properly: DBENGINE and DB_PATH.
+		Edit file: '/usr/local/etc/kamailio/kamctlrc'
+		</para>
+	        <programlisting  format="linespecific">
+		## database type: MYSQL, PGSQL, DB_BERKELEY, or DBTEXT, by default none is loaded
+		# DBENGINE=DB_BERKELEY
+		
+		## database path used by dbtext or db_berkeley
+		# DB_PATH="/usr/local/etc/kamailio/db_berkeley"
+		</programlisting>
+		
+		<para>
+		(Optional) Pre creation step- Customize your meta-data.
+		The DB files are initially seeded with necessary meta-data. 
+		This is a good time to review the meta-data section details,
+		before making modifications to your tables dbschema.
+		By default, the files are installed in '/usr/local/share/kamailio/db_berkeley/openser'
+		By default these tables are created Read/Write and without any journalling as 
+		shown. These settings can be modified on a per table basis.
+		Note: If you plan to use kambdb_recover, you must change the LOGFLAGS.
+		</para>
+	        <programlisting  format="linespecific">
+		METADATA_READONLY
+		0
+		METADATA_LOGFLAGS
+		0
+		</programlisting>
+		
+		
+		<para>
+		Execute kamdbctl - There are three (3) groups of tables you may need depending
+		on your situation.
+		</para>
+	        <programlisting  format="linespecific">
+		kamdbctl create   		(required)
+		kamdbctl presence 		(optional)
+		kamdbctl extra    		(optional)
+		</programlisting>
+		
+		<para>
+		Modify the Kamailio configuration file to use db_berkeley module. 
+		The database URL for modules must be the path to the directory where 
+		the Berkeley DB table-files are located, prefixed by "berkeley://", 
+		e.g., "berkeley:///usr/local/etc/kamailio/db_berkeley". 
+		</para>
+		
+		<para>
+		A couple other IMPORTANT things to consider are the 'db_mode' and the 'use_domain' 
+		modparams. The description of these parameters are found in usrloc documentation.
+		</para>
+		
+		<para>
+		Note on db_mode- 
+		The db_berkeley module will only journal the moment usrloc writes back
+		to the DB. The safest mode is mode 3 , since the db_berkeley journal files will always
+		be up-to-date. The main point is the db_mode vs. recovery by journal file interaction.
+		
+		Writing journal entries is 'best effort'. So if the hard drive becomes full, the
+		attempt to write a journal entry may fail.
+		</para>
+		
+		<para>
+		Note on use_domain-
+		The db_berkeley module will attempt natural joins when performing a query.
+		This is basically a lexigraphical string compare using the keys provided.
+		In most places in the db_berkeley dbschema (unless you customize), the domainname 
+		is identified as a natural key. 
+		Consider an example where use_domain = 0. In table subscriber, the db will be keying on 
+		'username|NULL' because the default value will be used when that key column is not provided.
+		This effectivly means that later queries must consistently use the username (w.o domain)
+		in order to find a result to that particular subscriber query.
+		The main point is 'use_domain' can not be changed once the db_berkeley is setup.
+		</para>
+		
+	</section>
+	
+	<section>
+	<title>Database Schema and Metadata</title>
+	
+	<para>
+	All Berkeley DB tables are created via the kamdbctl script. 
+	This section provides details as to the content and 
+	format of the DB file upon creation.
+	</para>
+
+	<para>
+	Since the Berkeley DB stores key value pairs, the database is seeded 
+	with a few meta-data rows . The keys to these rows must begin with 'METADATA'. 
+	Here is an example of table meta-data, taken from the table 'version'.
+	</para>
+
+	<para>
+	Note on reserved character- 
+	The '|' pipe character is used as a record delimiter within the 
+	Berkeley DB implementation and must not be present in any DB field.
+	</para>
+
+	<example>
+	<title>METADATA_COLUMNS</title>
+	<programlisting format="linespecific">
+METADATA_COLUMNS
+table_name(str) table_version(int)
+METADATA_KEY
+0
+	</programlisting>
+	</example>
+
+	<para>
+	In the above example, the row METADATA_COLUMNS defines the column names 
+	and type, and the row METADATA_KEY defines which column(s) form the key. 
+	Here the value of 0 indicates that column 0 is the key(ie table_name). 
+	With respect to column types, the db_berkeley modules only has the following 
+	types: string, str, int, double, and datetime. The default type is string, 
+	and is used when one of the others is not specified. The columns of the 
+	meta-data are delimited by whitespace.
+	</para>
+
+	<para>
+	The actual column data is stored as a string value, and delimited by 
+	the '|' pipe character. Since the code tokenizes on this delimiter, 
+	it is important that this character not appear in any valid data field. 
+	The following is the output of the 'db_berkeley.sh dump version' command. 
+	It shows contents of table 'version' in plain text.
+	</para>
+	
+	<example>
+	<title>contents of version table</title>
+	<programlisting format="linespecific">
+VERSION=3
+format=print
+type=hash
+h_nelem=21
+db_pagesize=4096
+HEADER=END
+ METADATA_READONLY
+ 1
+ address|
+ address|3
+ aliases|
+ aliases|1004
+ dbaliases|
+ dbaliases|1
+ domain|
+ domain|1
+ gw_grp|
+ gw_grp|1
+ gw|
+ gw|4
+ speed_dial|
+ speed_dial|2
+ subscriber|
+ subscriber|6
+ uri|
+ uri|1
+ METADATA_COLUMNS
+ table_name(str) table_version(int)
+ METADATA_KEY
+ 0
+ acc|
+ acc|4
+ grp|
+ grp|2
+ lcr|
+ lcr|2
+ location|
+ location|1004
+ missed_calls|
+ missed_calls|3
+ re_grp|
+ re_grp|1
+ silo|
+ silo|5
+ trusted|
+ trusted|4
+ usr_preferences|
+ usr_preferences|2
+DATA=END
+	</programlisting>
+	</example>
+	</section>
+	
+	<section>
+	<title>METADATA_COLUMNS (required)</title>
+	<para>
+	The METADATA_COLUMNS row contains the column names and types. 
+	Each is space delimited. Here is an example of the data taken from table subscriber :
+	</para>
+	
+	<example>
+	<title>METADATA_COLUMNS</title>
+	<programlisting>
+METADATA_COLUMNS
+username(str) domain(str) password(str) ha1(str) ha1b(str) first_name(str) last_name(str) email_address(str) datetime_created(datetime) timezone(str) rpid(str)
+ 	</programlisting>
+	</example>
+	
+	<para>
+	Related (hardcoded) limitations:
+	<itemizedlist>
+		<listitem>
+			<para>maximum of 32 columns per table.</para>
+		</listitem>
+		
+		<listitem>
+			<para>maximum tablename size is 64.</para>
+		</listitem>
+		
+		<listitem>
+			<para>maximum data length is 2048</para>
+		</listitem>
+	</itemizedlist>
+	</para>
+	
+	<para>
+	Currently supporting these five types: str, datetime, int, double, string.
+	</para>
+	
+</section>
+
+	<section>
+	<title>METADATA_KEYS (required)</title>
+	<para>
+	The METADATA_KEYS row indicates the indexes of the key columns, 
+	with respect to the order specified in METADATA_COLUMNS. 
+	Here is an example taken from table subscriber that brings up a good point:
+	</para>
+	
+	<example>
+	<title>METADATA_KEYS</title>
+	<programlisting>
+ METADATA_KEY
+ 0 1
+ 	</programlisting>
+	</example>
+
+ 	<para>
+	The point is that both the username and domain name are require 
+	as the key to this record. Thus, usrloc modparam 
+	use_domain = 1 must be set for this to work.
+	</para>
+	
+	</section>
+
+	<section>
+	<title>METADATA_READONLY (optional)</title>
+	<para>
+	The METADATA_READONLY row contains a boolean 0 or 1. 
+	By default, its value is 0. On startup the DB will 
+	open initially as read-write (loads metadata) and then if this 
+	is set=1, it will close and reopen as read only (ro). 
+	I found this useful because readonly has impacts on the 
+	internal db locking etc.
+	</para>
+	
+	</section>
+
+	<section>
+	<title>METADATA_LOGFLAGS (optional)</title>
+	<para>
+	The METADATA_LOGFLAGS row contains a bitfield that customizes the 
+	journaling on a per table basis. If not present the default value 
+	is taken as 0. Here are the masks so far (taken from bdb_lib.h):
+	</para>
+	
+	<example>
+	<title>METADATA_LOGFLAGS</title>
+	<programlisting>
+#define JLOG_NONE 0
+#define JLOG_INSERT 1
+#define JLOG_DELETE 2
+#define JLOG_UPDATE 4
+#define JLOG_STDOUT 8
+#define JLOG_SYSLOG 16
+	</programlisting>
+	</example>
+	
+	<para>
+	This means that if you want to journal INSERTS to local file and syslog the value 
+	should be set to 1+16=17. Or if you do not want to journal at all, set this to 0.
+	</para>
+	
+	</section>
+	
+	<section>
+	<title>DB Maintaince Script : kamdbctl </title>
+	
+	<para>
+	Use the kamdbctl script for maintaining Kamailio Berkeley DB tables.
+	This script assumes you have DBENGINE and DB_PATH setup correctly in kamctlrc.
+	Note Unsupported commands are- backup, restore, migrate, copy, serweb.
+	<example>
+	<title>kamdbctl</title>
+	<programlisting>
+usage: kamdbctl create   
+       kamdbctl presence 
+       kamdbctl extra    
+       kamdbctl drop     
+       kamdbctl reinit   
+       kamdbctl bdb list         (lists the underlying db files in DB_PATH)
+       kamdbctl bdb cat       db (prints the contents of db file to STDOUT in plain-text)
+       kamdbctl bdb swap      db (installs db.new by db -> db.old; db.new -> db)
+       kamdbctl bdb append    db datafile (appends data to a new instance of db; output DB_PATH/db.new)
+       kamdbctl bdb newappend db datafile (appends data to a new instance of db; output DB_PATH/db.new)
+	</programlisting>
+	</example>
+	</para>
+	</section>
+	
+	<section>
+	<title>DB Recovery : kambdb_recover</title>
+	<para>
+	The db_berkeley module uses the Concurrent Data Store (CDS) architecture. 
+	As such, no transaction or journaling is provided by the DB natively. 
+	The application kambdb_recover is specifically written to recover data from 
+	journal files that Kamailio creates.  
+	The kambdb_recover application requires an additional text file that contains 
+	the table schema.
+	</para>
+	
+	<para>
+	The schema is loaded with the '-s' option and is required for all operations.
+	Provide the path to the db_berkeley plain-text schema files. By default, these
+	install to '/usr/local/share/kamailio/db_berkeley/kamailio/'.
+	</para>
+	
+	<para>
+	The '-h' home option is the DB_PATH path. Unlike the Berkeley utilities, 
+	this application does not look for the DB_PATH environment variable, 
+	so you have to specify it. If not specified, it will assume the current 
+	working directory. The last argument is the operation. 
+	There are fundamentally only two operations- create and recover. 
+	</para>
+	
+	<para>
+	The following illustrates the four operations available to the administrator.
+	<example>
+	<title>kambdb_recover usage</title>
+	<programlisting>
+usage: ./kambdb_recover -s schemadir [-h home] [-c tablename]
+	This will create a brand new DB file with metadata.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-C all]
+	This will create all the core tables, each with metadata.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-r journal-file]
+	This will rebuild a DB and populate it with operation from journal-file. 
+	The table name is embedded in the journal-file name by convention.
+
+usage: ./kambdb_recover -s schemadir [-h home] [-R lastN]
+	This will iterate over all core tables enumerated. If journal files exist in 'home', 
+	a new DB file will be created and populated with the data found in the last N files. 
+	The files are 'replayed' in chronological order (oldest to newest). This 
+	allows the administrator to rebuild the db with a subset of all possible 
+	operations if needed. For example, you may only be interested in 
+	the last hours data in table location.
+	</programlisting>
+	</example>
+	</para>
+	
+	<para>
+	Important note- A corrupted DB file must be moved out of the way before kambdb_recover is executed.
+	</para>
+	
+	</section>
+	
+	<section>
+	<title>Known Limitations</title>
+	<para>
+	The Berkeley DB does not nativly support an autoincrement (or sequence) mechanism.
+	Consequently, this version does not support surragate keys in dbschema. These
+	are the id columns in the tables.
+	</para>
+	</section>
+	
+</chapter>
+