Jelajahi Sumber

- import berkeley database module into trunk
- Many thanks to William Quan from Cisco Systems for the contribution
- based on patch #1803180, renamed from berkely_db to db_berkeley
- Some work still remains:
- build berkley_db as own debian package
- add berkley_db to the xml database creation process to generate the
content in scripts/db_berkley/openser like the dbtext stuff
- evaluate if its possible to use the db_free_row and db_free_rows
functions for this module
- port to new logging system


git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@2844 689a6050-402a-0410-94f2-e92a70836424

Henning Westerholt 18 tahun lalu
melakukan
cac726a532
3 mengubah file dengan 1176 tambahan dan 0 penghapusan
  1. 20 0
      utils/db_berkeley/Makefile
  2. 1036 0
      utils/db_berkeley/bdb_recover.c
  3. 120 0
      utils/db_berkeley/bdb_recover.h

+ 20 - 0
utils/db_berkeley/Makefile

@@ -0,0 +1,20 @@
+# $Id: Makefile 882 2006-05-30 15:31:09Z miconda $
+#
+#  serunix Makefile
+# 
+
+include ../../Makefile.defs
+
+auto_gen=
+NAME=bdb_recover
+
+include ../../Makefile.sources
+
+# if you want to tune or reset flags
+#DEFS:=-DEXTRA_DEBUG  -I$(LOCALBASE)/BerkeleyDB.4.6/include
+DEFS +=-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
+        
+include ../../Makefile.rules

+ 1036 - 0
utils/db_berkeley/bdb_recover.c

@@ -0,0 +1,1036 @@
+/*
+ * $Id$
+ *
+ * recovery for berkeley_db module
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 "bdb_recover.h"
+
+/**
+* limitations:
+* 1. only support the core tables
+* 2. table version is not created with the data rows
+*/
+
+tbl_cache_p tables;
+char* schema_dir = NULL;
+char* db_home = NULL;
+const char *progname;
+
+/**
+ * main -- 
+ */
+int main(int argc, char* argv[])
+{
+	extern char* optarg;
+	extern int optind;
+	int ret, ch, i;
+	
+	ret = 0;
+	progname = argv[0];
+	
+	while ((ch = getopt(argc, argv, "s:h:c:C:r:R:")) != EOF)
+	switch (ch) 
+	{
+		case 's':
+		schema_dir = optarg;
+		load_schema(optarg);
+		break;
+		
+		case 'c': /*create <tablename> */
+		ret = create(optarg);
+		break;
+		
+		case 'C': /*Create all*/
+		ret = create_all();
+		break;
+		
+		case 'r': /*recover <filename> */
+		ret = recover(optarg);
+		break;
+		
+		case 'R': /*recover_all <MAXFILES> */
+		ret = sscanf(optarg,"%i", &i);
+		if(ret != 1) return -1;
+		ret = recover_all(i);
+		break;
+		
+		case 'h':
+		db_home = optarg;
+		break;
+		
+		case '?':
+		default:
+		return(usage());
+		
+	}
+	
+	argc -= optind;
+	argv += optind;
+	
+	/*free mem; close open files.*/
+	cleanup();
+	
+	return ret;
+}
+
+
+/**
+* usage -- 
+* 
+*/
+int usage(void)
+{
+	fprintf(stderr, "usage: %s %s\n", progname, 
+		"-s schemadir [-h home] [-c tablename]");
+	
+	fprintf(stderr, "usage: %s %s\n", progname, 
+		"-s schemadir [-h home] [-C all]");
+	
+	fprintf(stderr, "usage: %s %s\n", progname, 
+		"-s schemadir [-h home] [-r journal-file]");
+	
+	fprintf(stderr, "usage: %s %s\n", progname, 
+		"-s schemadir [-h home] [-R lastN]");
+	
+	return (EXIT_FAILURE);
+}
+
+
+/**
+ * create -- creates a Berkeley DB file with tablename (tn), along with
+ * 	 	the needed metadata.
+ * 		requires the schema data to be already parsed '-L' option.
+ */
+int create(char* tn)
+{
+	DB* db;
+	int rc;
+	tbl_cache_p tbc = NULL;
+	table_p tp = NULL;
+	rc = 0;
+	
+	tbc = get_table(tn);
+	if(!tbc)
+	{	fprintf(stderr, "[create] Table %s is not supported.\n",tn);
+		return 1;
+	}
+	
+	tp  = tbc->dtp;
+	db = get_db(tp);
+	
+	if(db)
+	{
+		printf("Created table %s\n",tn);
+		rc = 0;
+	}
+	else
+	{
+		fprintf(stderr, "[create] Failed to create table %s\n",tn);
+		rc = 1;
+	}
+	
+	return rc;
+}
+
+
+/**
+ * create_all -- creates a new Berkeley DB table for only the core tables
+ */
+int create_all(void)
+{	
+	tbl_cache_p _tbc = tables;
+	int rc;
+	rc = 0;
+	
+#ifdef EXTRA_DEBUG
+	time_t tim1 = time(NULL);
+	time_t tim2;
+#endif
+
+	while(_tbc)
+	{
+		if(_tbc->dtp)
+			if((rc = create(_tbc->dtp->name)) != 0 ) 
+				break;
+		_tbc = _tbc->next;
+	}
+	
+#ifdef EXTRA_DEBUG
+	tim2 = time(NULL);
+	i = tim2 - tim1;
+	printf("took %i sec\n", i);
+#endif
+
+	return rc;
+}
+
+
+/**
+ * file_list --
+ * returns a sorted linkedlist of all files in d
+ *
+ * parmameter d is the directory name
+ * parameter tn is optional,
+ * 	if tablename (tn) is specified returns only jnl files for tablename (tn)
+ *	else returns a sorted linkedlist of all files in d
+ * returns lnode_p 
+ *	the head linknode points to the latests file.
+ */
+lnode_p file_list(char* d, char* tn)
+{
+	DIR *dirp;
+	int i, j, len;
+	char *fn;
+	char *list[MAXFILES];
+	char dir[MAX_FILENAME_SIZE];
+	struct dirent *dp; 
+	lnode_p h,n;
+	
+	h = n = NULL;
+	i = j = 0;
+	
+	if(!d)
+	{
+		fprintf(stderr, "[file_list]: null path to schema files.\n");
+		return NULL;
+	}
+	
+	memset(dir, 0, MAX_FILENAME_SIZE);
+	strcpy(dir, d);
+	strcat(dir, "/");
+	//strcat(dir, ".");
+	dirp = opendir(dir);
+	
+	while ((dp = readdir(dirp)) != NULL)
+	{  j=0;
+	   if (i> (MAXFILES-1) )
+		   continue;
+	   
+	   fn = dp->d_name;
+	   
+	   if (fn[0] == '.')
+		   continue;
+	   
+	   if(tn)
+	   {
+		   /* only looking for jnl files */
+		   len = strlen(tn);
+		   if (!strstr(fn, ".jnl"))	continue;
+		   if (strncmp(fn, tn, len))	continue;
+	   }
+	   
+	   j = strlen(fn) +1;
+	   list[i] = malloc(sizeof(char) * j);
+	   memset(list[i], 0 , j);
+	   strcat(list[i], fn);
+	   i++;
+	}
+	
+	closedir(dirp);
+	qsort(list, i, sizeof(char*), compare);
+	
+	for(j=0;j<i;j++)
+	{
+		n = malloc(sizeof(lnode_t));
+		if(!n) return NULL;
+		n->prev=NULL;
+		n->p = list[j];
+		if(h) h->prev = n;
+		n->next = h;
+		h = n;
+	}
+	return h;
+}
+
+
+/** qsort C-string comparison function */
+int compare (const void *a, const void *b)
+{
+    const char **ia = (const char **)a;
+    const char **ib = (const char **)b;
+    return strcmp(*ia, *ib);
+}
+
+
+
+/**
+* recover -- given a journal filename, creates a new db w. metadata, and replays
+*	the events in journalized order. 
+*	Results in a new db containing the journaled data.
+*
+*	fn (filename) must be in the form:
+*	location-20070803175446.jnl 
+*/
+int recover(char* jfn)
+{
+
+#ifdef EXTRA_DEBUG
+	time_t tim1 = time(NULL);
+	time_t tim2;
+#endif
+
+	int len, i, cs, ci, cd, cu;
+	char *v, *s;
+	char line [MAX_ROW_SIZE];
+	char tn [MAX_TABLENAME_SIZE];
+	char fn [MAX_FILENAME_SIZE];
+	char op [7]; //INSERT, DELETE, UPDATE are all 7 char wide (w. null)
+	FILE * fp = NULL;
+	tbl_cache_p tbc = NULL;
+	table_p tp = NULL;
+	i = 0 ;
+	cs = ci = cd = cu = 0;
+	
+	if(!strstr(jfn, ".jnl"))
+	{
+		fprintf(stderr, "[recover]: Does NOT look like a journal file: %s.\n", jfn);
+		return 1;
+	}
+	
+	if(!db_home)
+	{
+		fprintf(stderr, "[recover]: null path to db_home.\n");
+		return 1;
+	}
+	
+	/*tablename tn*/
+	s = strchr(jfn, '-');
+	len = s - jfn;
+	strncpy(tn, jfn, len);
+	tn[len] = 0;
+	
+	/*create abs path to journal file relative to db_home*/
+	memset(fn, 0 , MAX_FILENAME_SIZE);
+	strcat(fn, db_home);
+	strcat(fn, "/");
+	strcat(fn, jfn);
+	
+	fp = fopen(fn, "r");
+	if(!fp) 
+	{
+		fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn);
+		return 2;
+	}
+	
+	tbc = get_table(tn);
+	if(!tbc)
+	{
+		fprintf(stderr, "[recover]: Table %s is not supported.\n",tn);
+		fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn);
+		return 2;
+	}
+	
+	tp  = tbc->dtp;
+	
+	if(!tbc || !tp)
+	{
+		fprintf(stderr, "[recover]: FAILED to get find metadata for : %s.\n", tn);
+		return 3;
+	}
+	
+	while ( fgets(line , MAX_ROW_SIZE, fp) != NULL )
+	{
+		len = strlen(line);
+		if(line[0] == '#' || line[0] == '\n') continue;
+		
+		if(len > 0) line[len-1] = 0; /*chomp trailing \n */
+		
+		v = strchr(line, '|');
+		len = v - line;
+		
+		strncpy(op, line, len);
+		op[len] = 0;
+		
+		switch( get_op(op, len) )
+		{
+		case INSERT:
+			v++; //now v points to data
+			len = strlen(v);
+			insert(tp, v, len);
+			ci++;
+			break;
+			
+		case UPDATE:
+			v++;
+			len = strlen(v);
+			update(tp, v, len);
+			cu++;
+			break;
+			
+		case DELETE:
+			//v is really the key
+			delete(tp, v, len);
+			cd++;
+			break;
+			
+		case UNKNOWN_OP:
+			fprintf(stderr,"[recover]: UnknownOP - Skipping ROW: %s\n",line);
+			cs++;
+			continue;
+		}
+		i++;
+	}
+	
+#ifdef EXTRA_DEBUG
+	printf("Processed journal file: %s.\n", jfn);
+	printf("INSERT   %i records.\n",ci);
+	printf("UPDATE   %i records.\n",cu);
+	printf("DELETE   %i records.\n",cd);
+	printf("SKIPed   %i records.\n",cs);
+	printf("------------------------\n");
+	printf("Total    %i records.\n",i);
+	
+	tim2 = time(NULL);
+	i = tim2 - tim1;
+	printf("took %i sec\n", i);
+#endif
+	
+	fclose(fp);
+	
+	return 0;
+}
+
+/**
+* recover_all -- Iterates over all core tables in enumerated order for recovery from 
+*	journal files (.jnl).
+*	The parm 'lastn' is the number of journal files needed to be recovered.
+*	Hardcoded to only find MAXFILES.
+*
+* 	e.g.
+*	25 journal files are present for the 'acc' table, however you only
+* 	want to restore the latest 3; so lastn=3.
+*/
+int recover_all(int lastn)
+{
+	lnode_p n, h;
+	tbl_cache_p _tbc = tables;
+	
+	if(MAXFILES < lastn) return 1;
+
+	if(!schema_dir)
+	{
+		fprintf(stderr, "[recover_all]: null path to schema files.\n");
+		return 1;
+	}
+
+	if(!db_home)
+	{
+		fprintf(stderr, "[recover_all]: null path to db_home.\n");
+		return 1;
+	}
+	
+	while(_tbc)
+	{	
+		int j;
+		
+		if(_tbc->dtp)
+			h = file_list(db_home, _tbc->dtp->name);
+		n = h;
+		
+		/*lastn; move to the oldest of the N*/
+		for(j=1;j<lastn;j++)
+			if(n && (n->next != NULL) )
+				n = n->next;
+		while(n)
+		{	printf("[recover_all] recovering file: %s\n",n->p);
+			if(recover(n->p))
+				fprintf(stderr, "[recover_all]: Error while recovering: [%s]\n. Continuing..\n",n->p);
+			n = n->prev;
+		}
+		
+		while(h) /*free mem*/
+		{	n = h->next;
+			free(h->p);
+			free(h);
+			h = n;
+		}
+		
+		_tbc = _tbc->next;
+	}
+	
+	return 0;
+}
+
+
+/**
+* extract_key -- uses the internal schema to extract the key from the data
+* 	row that was found in the journal.
+* caller provides inititialize memory for destination key (k).
+* data is provided ; key is filled in 
+*/
+int extract_key(table_p tp, char* k, char* d)
+{
+	char *s, *p;
+	char buf[MAX_ROW_SIZE];
+	int n, len;
+	
+	if(!tp || !k || !d) return  -1;
+	len=n=0;
+	p = k;
+	
+	/*copy data so we can tokenize w.o trampling */
+	len = strlen(d);
+	strncpy(buf, d, len);
+	buf[len] = 0;
+	
+	s = strtok(buf, "|"); 
+	while(s!=NULL && n<MAX_NUM_COLS) 
+	{
+		len = strlen(s);
+		if( (tp->ncols-1) > n)
+		{
+			if( tp->colp[n]->kflag )
+			{
+				strncpy(p, s, len);
+				p+=len;
+				
+				*p = '|';
+				p++;
+			}
+		}
+		
+		s=strtok(NULL, "|");
+		n++;
+	}
+	
+	*p = 0;
+	return 0;
+}
+
+/**
+* delete -- deletes a row from the db we are trying to rebuild 
+*/
+int delete(table_p tp, char* k, int len)
+{
+	DBT key;
+	DB *db;
+	
+	if(!tp || !k)	return 1;
+	if((db = get_db(tp)) == NULL)	return 2;
+	
+	memset(&key, 0, sizeof(DBT));
+	key.data = k;
+	key.ulen = MAX_ROW_SIZE;
+	key.size = len;
+	
+	if ( db->del(db, NULL, &key, 0))
+	{
+		fprintf(stderr, "[delete] FAILED --> [%.*s]  \n", len, k);
+		return 3;
+	}
+	
+	return 0;
+}
+
+
+/**
+* _insert -- inserts a new row in to the db we are trying to rebuild
+* 	I needed this to directly insert metadata when the db is created.
+*/
+int _insert(DB* db, char* k, char* v, int klen, int vlen)
+{
+	DBT key, data;
+	
+	if(!db || !k || !v) 	return 1;
+	
+	memset(&key,  0, sizeof(DBT));
+	key.data = k;
+	key.ulen = MAX_ROW_SIZE;
+	key.size = klen;
+	
+	memset(&data, 0, sizeof(DBT));
+	data.data = v;
+	data.ulen = MAX_ROW_SIZE;
+	data.size = vlen;
+	if (db->put(db, NULL, &key, &data, 0))
+	{
+		fprintf(stderr, "[insert] FAILED --> [%.*s]  \n", vlen, v);
+		return 1;
+	}
+	return 0;
+}
+
+
+/**
+* insert -- given the data row (v) and its length (vlen), we build the corresponding
+* 	key, and insert the data in to the db.
+*	This will over-right the value if already present.
+*/
+int insert(table_p tp, char* v, int vlen)
+{
+	char k[MAX_ROW_SIZE];
+	int rc, klen;
+	DB *db;
+	
+	if(!tp || !v) 	return 1;
+	if((db = get_db(tp)) == NULL)	return 2;
+	
+	memset(k,0,MAX_ROW_SIZE);
+	if( extract_key(tp, k, v) )
+	{
+		fprintf(stderr, "[insert] failed to extract key for row: %.*s",vlen, v);
+		return 2;
+	}
+	
+	klen = strlen(k);
+	rc = _insert(db, k, v, klen, vlen);
+	
+	return rc;
+}
+
+
+/**
+* update -- given the data row (v) and its length (vlen), we build the corresponding
+* 	key, and update the data in the db.
+*	This is implemented as DELETE + INSERT.
+*/
+int update(table_p tp, char* v, int len)
+{
+	char k[MAX_ROW_SIZE];
+	
+	if(!tp || !v)	return 1;
+	
+	memset(k,0,MAX_ROW_SIZE);
+	if( extract_key(tp, k, v) )
+	{
+		fprintf(stderr, "[update] failed to extract key for row: %.*s",len, v);
+		return 2;
+	}
+	
+/*	if( delete(tp, k, strlen(k)) )	return 3;  */
+	if( insert(tp, v, len) )	return 4;
+	return 0;
+}
+
+
+
+/**
+* get_op -- used to convert the string operation name to an enumerated op
+*/
+int get_op(char* op, int len)
+{
+	if((len==6) && strstr("INSERT",op) )	return INSERT;
+	if((len==6) && strstr("UPDATE",op) )	return UPDATE;
+	if((len==6) && strstr("DELETE",op) )	return DELETE;
+	
+	return UNKNOWN_OP;
+}
+
+
+/**
+* load_schema -- sets up the internal representation of the schema.
+*/
+int load_schema(char* d)
+{	int rc;
+	char *tn;
+	char line1 [MAX_ROW_SIZE];
+	char line2 [MAX_ROW_SIZE];
+	char fn [MAX_FILENAME_SIZE];
+	tbl_cache_p tbc = NULL;
+	table_p tp = NULL;
+	FILE * fp = NULL;
+	lnode_p h,n;
+	
+	rc=0;
+	h = n = NULL;
+	
+	if(!d)
+	{
+		fprintf(stderr, "[load_schema]: null path to schema files.\n");
+		return 1;
+	}
+	
+	tables = (tbl_cache_p)malloc(sizeof(tbl_cache_t));
+	if(!tables)	return 1;
+	
+	h = file_list(d, NULL);
+	
+	while(h)
+	{
+		n = h->next;
+		
+		/*create abs path to journal file (relative to db_home) */
+		memset(fn, 0 , MAX_FILENAME_SIZE);
+		strcat(fn, d);
+		strcat(fn, "/");
+		strcat(fn, h->p);
+		
+		fp = fopen(fn, "r");
+		if(!fp)
+		{
+			fprintf(stderr, "[load_schema]: FAILED to load schema file: %s.\n", h->p);
+			break;
+		}
+		
+		tn = h->p;
+		tbc = get_table(tn);
+		if(!tbc)
+		{
+			fprintf(stderr, "[load_schema]: Table %s is not supported.\n",tn);
+			fprintf(stderr, "[load_schema]: FAILED to load data for table: %s.\n", tn);
+			goto done;
+		}
+		
+		tp = tbc->dtp;
+		
+		while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL )
+		{
+			if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL )
+			{
+				if(strstr(line1, METADATA_COLUMNS))
+				{
+					if(0!=load_metadata_columns(tp, line2))
+					{
+						fprintf(stderr, "[load_schema]: FAILED to load METADATA COLS in table: %s.\n", tn);
+						goto done;
+					}
+				}
+				
+				if(strstr(line1, METADATA_KEY))
+				{
+					if(0!=load_metadata_key(tp, line2))
+					{
+						fprintf(stderr, "[load_schema]: FAILED to load METADATA KEYS in table: %s.\n", tn);
+						goto done;
+					}
+				}
+			}
+			else
+			{
+				fprintf(stderr, "[load_schema]: FAILED to read schema value in table: %s.\n", tn);
+				goto done;
+			}
+			
+		}
+done:		
+		fclose(fp);
+		h = n;
+	}
+	
+	while(h) /*free mem*/
+	{	n = h->next;
+		free(h->p);
+		free(h);
+		h = n;
+	}
+	
+	return rc;
+}
+
+
+
+/**
+* get_table -- return pointer to lazy initialized table struct
+*/
+tbl_cache_p get_table(char *_s)
+{
+	tbl_cache_p _tbc = tables;
+	table_p _tp = NULL;
+
+	while(_tbc)
+	{
+		if(_tbc->dtp)
+		{
+			if(_tbc->dtp->name
+			&& !strcmp(_tbc->dtp->name,_s))
+			{
+				return _tbc;
+			}
+		}
+		_tbc = _tbc->next;
+	}
+	
+	_tbc = (tbl_cache_p)malloc(sizeof(tbl_cache_t));
+	if(!_tbc)
+		return NULL;
+	
+	_tp = create_table(_s);
+	
+	if(!_tp)
+	{
+		fprintf(stderr, "[get_table]: failed to create table.\n");
+		free(_tbc);
+		return NULL;
+	}
+	
+	_tbc->dtp = _tp;
+	
+	if(tables)
+		(tables)->prev = _tbc;
+	
+	_tbc->next = tables;
+	tables = _tbc;
+
+	return _tbc;
+}
+
+
+/**
+* create_table -- returns an initialed table struct
+*/
+table_p create_table(char *_s)
+{
+	int i;
+	table_p tp = NULL;
+	
+	tp = (table_p)malloc(sizeof(table_t));
+	if(!tp)	return NULL;
+	
+	i=strlen(_s)+1;
+	tp->name = (char*)malloc(i*sizeof(char));
+	strncpy(tp->name, _s, i);
+	
+	tp->ncols=0;
+	tp->nkeys=0;
+	tp->ro=0;
+	tp->logflags=0;
+	tp->db = NULL;
+	
+	for(i=0;i<MAX_NUM_COLS;i++)
+		tp->colp[i] = NULL;
+	
+	return tp;
+}
+
+
+/**
+* load_metadata_columns -- parses the METADATA_COLUMNS line into the internal
+* 	representation.
+*/
+int load_metadata_columns(table_p _tp, char* line)
+{
+	int ret,n,len;
+	char *s = NULL;
+	char cn[64], ct[16];
+	column_p col;
+	ret = n = len = 0;
+	
+	if(!_tp) return -1;
+	if(_tp->ncols!=0) return 0;
+	
+	/* eg: line = "table_name(str) table_version(int)" */
+	s = strtok(line, " \t");
+	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) malloc(sizeof(column_t));
+		if(!col)
+		{	fprintf(stderr, "load_metadata_columns: out of memory \n");
+			return -1;
+		}
+		
+		/* set name*/
+		len = strlen( cn )+1;
+		col->name = (char*)malloc(len * sizeof(char));
+		strcpy(col->name, cn );
+		
+		/* set type*/
+		len = strlen( ct )+1;
+		col->type = (char*)malloc(len * sizeof(char));
+		strcpy(col->type, ct );
+		
+		_tp->colp[n] = col;
+		n++;
+		_tp->ncols++;
+		s=strtok(NULL, " \t");
+	}
+
+	return 0;
+}
+
+
+/**
+* load_metadata_key -- parses the METADATA_KEY line into the internal
+* 	representation.
+*/
+int load_metadata_key(table_p _tp, char* line)
+{
+	int ret,n,ci;
+	char *s = NULL;
+	ret = n = ci = 0;
+	
+	if(!_tp)return -1;
+	
+	s = strtok(line, " \t");
+	while(s!=NULL && n< _tp->ncols) 
+	{
+		ret = sscanf(s,"%i", &ci);
+		if(ret != 1) return -1;
+		if( _tp->colp[ci] ) 
+		{	_tp->colp[ci]->kflag = 1;
+			_tp->nkeys++;
+		}
+		n++;
+		s=strtok(NULL, " ");
+	}
+	
+	return 0;
+}
+
+
+/**
+* get_db -- lazy initialized DB access
+*	Its like this so we get new db files only for the tables that have
+*	journal files.
+*	The db file on disk will be named:
+*		<tablename>.new
+*/
+DB* get_db(table_p tp)
+{
+	int rc;
+	DB* db;
+	char dfn[MAX_FILENAME_SIZE];
+	
+	if( !tp) 	return NULL;
+	if( tp->db) 	return tp->db;
+	
+	memset(dfn, 0, MAX_FILENAME_SIZE);
+	if(db_home)
+	{
+		strcpy(dfn, db_home);
+		strcat(dfn, "/");
+	}
+	
+	/*creation of DB follows*/
+	strcat(dfn, tp->name);
+	
+	if ((rc = db_create(&db, NULL, 0)) != 0)
+	{ 
+		fprintf(stderr, "[create_table]: error db_create for table: %s.\n",dfn);
+		return NULL;
+	}
+	
+	if ((rc = db->open(db, NULL, dfn, NULL, DB_HASH, DB_CREATE, 0664)) != 0)
+	{ 
+		fprintf(stderr, "[create_table]: error opening %s.\n",dfn);
+		fprintf(stderr, "[create_table]: error msg: %s.\n",db_strerror(rc));
+		return NULL;
+	}
+	tp->db = db;
+	
+	import_schema(tp);
+	
+	return db;
+}
+
+
+/**
+*/
+int import_schema(table_p tp)
+{
+	int rc, len1, len2;
+	char line1 [MAX_ROW_SIZE];
+	char line2 [MAX_ROW_SIZE];
+	char fn [MAX_FILENAME_SIZE];
+	FILE * fp = NULL;
+	rc = 0;
+	
+	if(!schema_dir)
+	{
+		fprintf(stderr, "[import_schema]: null schema dir.\n");
+		return 1;
+	}
+	
+	if(!tp)
+	{
+		fprintf(stderr, "[import_schema]: null table parameter.\n");
+		return 1;
+	}
+	
+	/*create abs path to journal file (relative to db_home) */
+	memset(fn, 0 , MAX_FILENAME_SIZE);
+	strcat(fn, schema_dir);
+	strcat(fn, "/");
+	strcat(fn, tp->name);
+	
+	fp = fopen(fn, "r");
+	if(!fp)
+	{
+		fprintf(stderr, "[import_schema]: FAILED to open def schema file: %s.\n", fn);
+		return 1;
+	}
+
+	while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL )
+	{
+		if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL )
+		{
+			len1 = strlen(line1)-1;
+			len2 = strlen(line2)-1;
+			line1[len1] = 0;
+			line2[len2] = 0;
+			
+			if((rc = _insert(tp->db, line1, line2, len1, len2) )!=0)
+			{
+				fprintf(stderr, "[import_schema]: FAILED to write schema def into table: %s.\n", tp->name);
+				goto done;
+			}
+		}
+		else
+		{
+			fprintf(stderr, "[import_schema]: FAILED to read schema def value in table: %s.\n", tp->name);
+			goto done;
+		}
+		
+	}
+done:		
+	fclose(fp);
+	return rc;
+}
+
+
+
+/**
+* cleanup -- frees memory; closes any files.
+*/
+void cleanup(void)
+{
+	//cleanup
+	while(tables)
+	{	int i;
+		tbl_cache_p n   = tables->next;
+		table_p     tp  = tables->dtp;
+		if(tp)
+		{
+			free(tp->name);
+			for(i=0;i< tp->ncols;i++)
+			{
+				free(tp->colp[i]->name);
+				free(tp->colp[i]->type);
+				free(tp->colp[i]);
+			}
+			
+			if(tp->db)
+				tp->db->close(tp->db, 0);
+			
+			free(tp);
+		}
+		free(tables);
+		tables = n;
+	}
+}
+

+ 120 - 0
utils/db_berkeley/bdb_recover.h

@@ -0,0 +1,120 @@
+/*
+ * $Id$
+ *
+ * recovery for berkeley_db module
+ * 
+ * Copyright (C) 2007 Cisco Systems
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 <stdlib.h>
+#include <dirent.h>
+#include <time.h>
+#include <db.h>
+
+/*max number of journal files that we are reading*/
+#define MAXFILES 64
+
+/*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 MAX_FILENAME_SIZE 512
+
+#define METADATA_KEY "METADATA_KEY"
+#define METADATA_COLUMNS "METADATA_COLUMNS"
+
+/*operations*/
+enum 
+{
+	INSERT,
+	UPDATE,
+	DELETE,
+	UNKNOWN_OP
+};
+
+
+typedef struct _lnode
+{
+	char* p;
+	struct _lnode *prev;
+	struct _lnode *next;
+} lnode_t, *lnode_p;
+
+
+typedef struct _column
+{
+	char* name;
+	char* type;
+	int kflag;
+} column_t, *column_p;
+
+
+typedef struct _table
+{
+	char* name;
+	column_p colp [MAX_NUM_COLS];
+	int ncols;
+	int nkeys;
+	int ro;
+	int logflags;
+	DB* db;
+} table_t, *table_p;
+
+
+typedef struct _tbl_cache
+{
+	table_p dtp;
+	struct _tbl_cache *prev;
+	struct _tbl_cache *next;
+} tbl_cache_t, *tbl_cache_p;
+
+
+int usage(void);
+DB* get_db(table_p tp);
+int get_op(char* op, int len);
+int delete(table_p tp, char* v, int len);
+int insert(table_p tp, char* v, int len);
+int _insert(DB* db, char* k, char* v, int klen, int vlen);
+int update(table_p tp, char* v, int len);
+int create(char* tn);
+int _version(DB* db);
+int create_all(void);
+int recover(char* tn);
+int recover_all(int lastn);
+lnode_p file_list(char* d, char* tn);
+int compare (const void *a, const void *b);
+int extract_key(table_p tp, char* key, char* data);
+int load_schema(char* dir);
+tbl_cache_p get_table(char *s);
+table_p create_table(char *_s);
+int load_metadata_columns(table_p _tp, char* line);
+int load_metadata_key(table_p _tp, char* line);
+int import_schema(table_p tp);
+void cleanup(void);