1 /*
2  * $Id$
3  *
4  * recovery for berkeley_db module
5  * Copyright (C) 2007 Cisco Systems
6  *
7  * This file is part of Kamailio, a free SIP server.
8  *
9  * Kamailio is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version
13  *
14  * Kamailio is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  * History:
24  * --------
25  * 2007-09-19  genesis (wiquan)
26  */
27 
28 #include <unistd.h>
29 #include "kambdb_recover.h"
30 
31 tbl_cache_p tables;
32 char* schema_dir = NULL;
33 char* db_home = NULL;
34 const char *progname;
35 
36 /**
37  * main --
38  */
main(int argc,char * argv[])39 int main(int argc, char* argv[])
40 {
41 	int ret, ch, i;
42 
43 	ret = 0;
44 	progname = argv[0];
45 
46 	while ((ch = getopt(argc, argv, "s:h:c:C:r:R:")) != EOF)
47 	switch (ch)
48 	{
49 		case 's':
50 		schema_dir = optarg;
51 		load_schema(optarg);
52 		break;
53 
54 		case 'c': /*create <tablename> */
55 		ret = create(optarg);
56 		break;
57 
58 		case 'C': /*Create all*/
59 		ret = create_all();
60 		break;
61 
62 		case 'r': /*recover <filename> */
63 		ret = recover(optarg);
64 		break;
65 
66 		case 'R': /*recover_all <MAXFILES> */
67 		ret = sscanf(optarg,"%i", &i);
68 		if(ret != 1) return -1;
69 		ret = recover_all(i);
70 		break;
71 
72 		case 'h':
73 		db_home = optarg;
74 		break;
75 
76 		case '?':
77 		default:
78 		return(usage());
79 
80 	}
81 
82 	argc -= optind;
83 	argv += optind;
84 
85 	/*free mem; close open files.*/
86 	cleanup();
87 
88 	return ret;
89 }
90 
91 
92 /**
93 * usage --
94 *
95 */
usage(void)96 int usage(void)
97 {
98 	fprintf(stderr, "usage: %s %s\n", progname,
99 		"-s schemadir [-h home] [-c tablename]");
100 
101 	fprintf(stderr, "usage: %s %s\n", progname,
102 		"-s schemadir [-h home] [-C all]");
103 
104 	fprintf(stderr, "usage: %s %s\n", progname,
105 		"-s schemadir [-h home] [-r journal-file]");
106 
107 	fprintf(stderr, "usage: %s %s\n", progname,
108 		"-s schemadir [-h home] [-R lastN]");
109 
110 	return (EXIT_FAILURE);
111 }
112 
113 
114 /**
115  * create -- creates a Berkeley DB file with tablename (tn), along with
116  * 	 	the needed metadata.
117  * 		requires the schema data to be already parsed '-L' option.
118  */
create(char * tn)119 int create(char* tn)
120 {
121 	DB* db;
122 	int rc;
123 	tbl_cache_p tbc = NULL;
124 	table_p tp = NULL;
125 	rc = 0;
126 
127 	tbc = get_table(tn);
128 	if(!tbc)
129 	{	fprintf(stderr, "[create] Table %s is not supported.\n",tn);
130 		return 1;
131 	}
132 
133 	tp  = tbc->dtp;
134 	db = get_db(tp);
135 
136 	if(db)
137 	{
138 		printf("Created table %s\n",tn);
139 		rc = 0;
140 	}
141 	else
142 	{
143 		fprintf(stderr, "[create] Failed to create table %s\n",tn);
144 		rc = 1;
145 	}
146 
147 	return rc;
148 }
149 
150 
151 /**
152  * create_all -- creates a new Berkeley DB table for only the core tables
153  */
create_all(void)154 int create_all(void)
155 {
156 	tbl_cache_p _tbc = tables;
157 	int rc;
158 	rc = 0;
159 
160 #ifdef EXTRA_DEBUG
161 	time_t tim1 = time(NULL);
162 	time_t tim2;
163 #endif
164 
165 	while(_tbc)
166 	{
167 		if(_tbc->dtp)
168 			if((rc = create(_tbc->dtp->name)) != 0 )
169 				break;
170 		_tbc = _tbc->next;
171 	}
172 
173 #ifdef EXTRA_DEBUG
174 	tim2 = time(NULL);
175 	int i = tim2 - tim1;
176 	printf("took %i sec\n", i);
177 #endif
178 
179 	return rc;
180 }
181 
182 
183 /**
184  * file_list --
185  * returns a sorted linkedlist of all files in d
186  *
187  * parmameter d is the directory name
188  * parameter tn is optional,
189  * 	if tablename (tn) is specified returns only jnl files for tablename (tn)
190  *	else returns a sorted linkedlist of all files in d
191  * returns lnode_p
192  *	the head linknode points to the latests file.
193  */
file_list(char * d,char * tn)194 lnode_p file_list(char* d, char* tn)
195 {
196 	DIR *dirp;
197 	int i, j, len;
198 	char *fn;
199 	char *list[MAXFILES];
200 	char dir[MAX_FILENAME_SIZE];
201 	struct dirent *dp;
202 	lnode_p h,n;
203 
204 	h = n = NULL;
205 	i = j = 0;
206 
207 	if(!d)
208 	{
209 		fprintf(stderr, "[file_list]: null path to schema files.\n");
210 		return NULL;
211 	}
212 
213 	memset(dir, 0, MAX_FILENAME_SIZE);
214 	strcpy(dir, d);
215 	strcat(dir, "/");
216 	//strcat(dir, ".");
217 	dirp = opendir(dir);
218 
219 	while ((dp = readdir(dirp)) != NULL)
220 	{  j=0;
221 	   if (i> (MAXFILES-1) )
222 		   continue;
223 
224 	   fn = dp->d_name;
225 
226 	   if (fn[0] == '.')
227 		   continue;
228 
229 	   if(tn)
230 	   {
231 		   /* only looking for jnl files */
232 		   len = strlen(tn);
233 		   if (!strstr(fn, ".jnl"))	continue;
234 		   if (strncmp(fn, tn, len))	continue;
235 	   }
236 
237 	   j = strlen(fn) +1;
238 	   list[i] = malloc(sizeof(char) * j);
239 	   memset(list[i], 0 , j);
240 	   strcat(list[i], fn);
241 	   i++;
242 	}
243 
244 	closedir(dirp);
245 	qsort(list, i, sizeof(char*), compare);
246 
247 	for(j=0;j<i;j++)
248 	{
249 		n = malloc(sizeof(lnode_t));
250 		if(!n) return NULL;
251 		n->prev=NULL;
252 		n->p = list[j];
253 		if(h) h->prev = n;
254 		n->next = h;
255 		h = n;
256 	}
257 	return h;
258 }
259 
260 
261 /** qsort C-string comparison function */
compare(const void * a,const void * b)262 int compare (const void *a, const void *b)
263 {
264     const char **ia = (const char **)a;
265     const char **ib = (const char **)b;
266     return strcmp(*ia, *ib);
267 }
268 
269 
270 
271 /**
272 * recover -- given a journal filename, creates a new db w. metadata, and replays
273 *	the events in journalized order.
274 *	Results in a new db containing the journaled data.
275 *
276 *	fn (filename) must be in the form:
277 *	location-20070803175446.jnl
278 */
recover(char * jfn)279 int recover(char* jfn)
280 {
281 
282 #ifdef EXTRA_DEBUG
283 	time_t tim1 = time(NULL);
284 	time_t tim2;
285 #endif
286 
287 	int len, i, cs, ci, cd, cu;
288 	char *v, *s;
289 	char line [MAX_ROW_SIZE];
290 	char tn [MAX_TABLENAME_SIZE];
291 	char fn [MAX_FILENAME_SIZE];
292 	char op [7]; //INSERT, DELETE, UPDATE are all 7 char wide (w. null)
293 	FILE * fp = NULL;
294 	tbl_cache_p tbc = NULL;
295 	table_p tp = NULL;
296 	i = 0 ;
297 	cs = ci = cd = cu = 0;
298 
299 	if(!strstr(jfn, ".jnl"))
300 	{
301 		fprintf(stderr, "[recover]: Does NOT look like a journal file: %s.\n", jfn);
302 		return 1;
303 	}
304 
305 	if(!db_home)
306 	{
307 		fprintf(stderr, "[recover]: null path to db_home.\n");
308 		return 1;
309 	}
310 
311 	/*tablename tn*/
312 	s = strchr(jfn, '-');
313 	len = s - jfn;
314 	strncpy(tn, jfn, len);
315 	tn[len] = 0;
316 
317 	/*create abs path to journal file relative to db_home*/
318 	memset(fn, 0 , MAX_FILENAME_SIZE);
319 	strcat(fn, db_home);
320 	strcat(fn, "/");
321 	strcat(fn, jfn);
322 
323 	fp = fopen(fn, "r");
324 	if(!fp)
325 	{
326 		fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn);
327 		return 2;
328 	}
329 
330 	tbc = get_table(tn);
331 	if(!tbc)
332 	{
333 		fprintf(stderr, "[recover]: Table %s is not supported.\n",tn);
334 		fprintf(stderr, "[recover]: FAILED to load journal file: %s.\n", jfn);
335 		fclose(fp);
336 		return 2;
337 	}
338 
339 	if(!tbc || !tbc->dtp)
340 	{
341 		fprintf(stderr, "[recover]: FAILED to get find metadata for : %s.\n", tn);
342 		fclose(fp);
343 		return 3;
344 	}
345 
346 	tp  = tbc->dtp;
347 
348 	while ( fgets(line , MAX_ROW_SIZE, fp) != NULL )
349 	{
350 		len = strlen(line);
351 		if(line[0] == '#' || line[0] == '\n') continue;
352 
353 		if(len > 0) line[len-1] = 0; /*chomp trailing \n */
354 
355 		v = strchr(line, '|');
356 		len = v - line;
357 
358 		strncpy(op, line, len);
359 		op[len] = 0;
360 
361 		switch( get_op(op, len) )
362 		{
363 		case INSERT:
364 			v++; //now v points to data
365 			len = strlen(v);
366 			insert(tp, v, len);
367 			ci++;
368 			break;
369 
370 		case UPDATE:
371 			v++;
372 			len = strlen(v);
373 			update(tp, v, len);
374 			cu++;
375 			break;
376 
377 		case DELETE:
378 			//v is really the key
379 			delete(tp, v, len);
380 			cd++;
381 			break;
382 
383 		case UNKNOWN_OP:
384 			fprintf(stderr,"[recover]: UnknownOP - Skipping ROW: %s\n",line);
385 			cs++;
386 			continue;
387 		}
388 		i++;
389 	}
390 
391 #ifdef EXTRA_DEBUG
392 	printf("Processed journal file: %s.\n", jfn);
393 	printf("INSERT   %i records.\n",ci);
394 	printf("UPDATE   %i records.\n",cu);
395 	printf("DELETE   %i records.\n",cd);
396 	printf("SKIPed   %i records.\n",cs);
397 	printf("------------------------\n");
398 	printf("Total    %i records.\n",i);
399 
400 	tim2 = time(NULL);
401 	i = tim2 - tim1;
402 	printf("took %i sec\n", i);
403 #endif
404 
405 	fclose(fp);
406 
407 	return 0;
408 }
409 
410 /**
411 * recover_all -- Iterates over all core tables in enumerated order for recovery from
412 *	journal files (.jnl).
413 *	The parm 'lastn' is the number of journal files needed to be recovered.
414 *	Hardcoded to only find MAXFILES.
415 *
416 * 	e.g.
417 *	25 journal files are present for the 'acc' table, however you only
418 * 	want to restore the latest 3; so lastn=3.
419 */
recover_all(int lastn)420 int recover_all(int lastn)
421 {
422 	lnode_p n, h;
423 	tbl_cache_p _tbc = tables;
424 
425 	if(MAXFILES < lastn) return 1;
426 
427 	if(!schema_dir)
428 	{
429 		fprintf(stderr, "[recover_all]: null path to schema files.\n");
430 		return 1;
431 	}
432 
433 	if(!db_home)
434 	{
435 		fprintf(stderr, "[recover_all]: null path to db_home.\n");
436 		return 1;
437 	}
438 
439 	while(_tbc)
440 	{
441 		int j;
442 
443 		if(_tbc->dtp)
444 			h = file_list(db_home, _tbc->dtp->name);
445 		n = h;
446 
447 		/*lastn; move to the oldest of the N*/
448 		for(j=1;j<lastn;j++)
449 			if(n && (n->next != NULL) )
450 				n = n->next;
451 		while(n)
452 		{	printf("[recover_all] recovering file: %s\n",n->p);
453 			if(recover(n->p))
454 				fprintf(stderr, "[recover_all]: Error while recovering: [%s]\n. Continuing..\n",n->p);
455 			n = n->prev;
456 		}
457 
458 		while(h) /*free mem*/
459 		{	n = h->next;
460 			free(h->p);
461 			free(h);
462 			h = n;
463 		}
464 
465 		_tbc = _tbc->next;
466 	}
467 
468 	return 0;
469 }
470 
471 
472 /**
473 * extract_key -- uses the internal schema to extract the key from the data
474 * 	row that was found in the journal.
475 * caller provides inititialize memory for destination key (k).
476 * data is provided ; key is filled in
477 */
extract_key(table_p tp,char * k,char * d)478 int extract_key(table_p tp, char* k, char* d)
479 {
480 	char *s, *p;
481 	char buf[MAX_ROW_SIZE];
482 	int n, len;
483 
484 	if(!tp || !k || !d) return  -1;
485 	len=n=0;
486 	p = k;
487 
488 	/*copy data so we can tokenize w.o trampling */
489 	len = strlen(d);
490 	strncpy(buf, d, len);
491 	buf[len] = 0;
492 
493 	s = strtok(buf, "|");
494 	while(s!=NULL && n<MAX_NUM_COLS)
495 	{
496 		len = strlen(s);
497 		if( (tp->ncols-1) > n)
498 		{
499 			if( tp->colp[n]->kflag )
500 			{
501 				strncpy(p, s, len);
502 				p+=len;
503 
504 				*p = '|';
505 				p++;
506 			}
507 		}
508 
509 		s=strtok(NULL, "|");
510 		n++;
511 	}
512 
513 	*p = 0;
514 	return 0;
515 }
516 
517 /**
518 * delete -- deletes a row from the db we are trying to rebuild
519 */
delete(table_p tp,char * k,int len)520 int delete(table_p tp, char* k, int len)
521 {
522 	DBT key;
523 	DB *db;
524 
525 	if(!tp || !k)	return 1;
526 	if((db = get_db(tp)) == NULL)	return 2;
527 
528 	memset(&key, 0, sizeof(DBT));
529 	key.data = k;
530 	key.ulen = MAX_ROW_SIZE;
531 	key.size = len;
532 
533 	if ( db->del(db, NULL, &key, 0))
534 	{
535 		fprintf(stderr, "[delete] FAILED --> [%.*s]  \n", len, k);
536 		return 3;
537 	}
538 
539 	return 0;
540 }
541 
542 
543 /**
544 * _insert -- inserts a new row in to the db we are trying to rebuild
545 * 	I needed this to directly insert metadata when the db is created.
546 */
_insert(DB * db,char * k,char * v,int klen,int vlen)547 int _insert(DB* db, char* k, char* v, int klen, int vlen)
548 {
549 	DBT key, data;
550 
551 	if(!db || !k || !v) 	return 1;
552 
553 	memset(&key,  0, sizeof(DBT));
554 	key.data = k;
555 	key.ulen = MAX_ROW_SIZE;
556 	key.size = klen;
557 
558 	memset(&data, 0, sizeof(DBT));
559 	data.data = v;
560 	data.ulen = MAX_ROW_SIZE;
561 	data.size = vlen;
562 	if (db->put(db, NULL, &key, &data, 0))
563 	{
564 		fprintf(stderr, "[insert] FAILED --> [%.*s]  \n", vlen, v);
565 		return 1;
566 	}
567 	return 0;
568 }
569 
570 
571 /**
572 * insert -- given the data row (v) and its length (vlen), we build the corresponding
573 * 	key, and insert the data in to the db.
574 *	This will over-right the value if already present.
575 */
insert(table_p tp,char * v,int vlen)576 int insert(table_p tp, char* v, int vlen)
577 {
578 	char k[MAX_ROW_SIZE];
579 	int rc, klen;
580 	DB *db;
581 
582 	if(!tp || !v) 	return 1;
583 	if((db = get_db(tp)) == NULL)	return 2;
584 
585 	memset(k,0,MAX_ROW_SIZE);
586 	if( extract_key(tp, k, v) )
587 	{
588 		fprintf(stderr, "[insert] failed to extract key for row: %.*s",vlen, v);
589 		return 2;
590 	}
591 
592 	klen = strlen(k);
593 	rc = _insert(db, k, v, klen, vlen);
594 
595 	return rc;
596 }
597 
598 
599 /**
600 * update -- given the data row (v) and its length (vlen), we build the corresponding
601 * 	key, and update the data in the db.
602 *	This is implemented as DELETE + INSERT.
603 */
update(table_p tp,char * v,int len)604 int update(table_p tp, char* v, int len)
605 {
606 	char k[MAX_ROW_SIZE];
607 
608 	if(!tp || !v)	return 1;
609 
610 	memset(k,0,MAX_ROW_SIZE);
611 	if( extract_key(tp, k, v) )
612 	{
613 		fprintf(stderr, "[update] failed to extract key for row: %.*s",len, v);
614 		return 2;
615 	}
616 
617 /*	if( delete(tp, k, strlen(k)) )	return 3;  */
618 	if( insert(tp, v, len) )	return 4;
619 	return 0;
620 }
621 
622 
623 
624 /**
625 * get_op -- used to convert the string operation name to an enumerated op
626 */
get_op(char * op,int len)627 int get_op(char* op, int len)
628 {
629 	if((len==6) && strstr("INSERT",op) )	return INSERT;
630 	if((len==6) && strstr("UPDATE",op) )	return UPDATE;
631 	if((len==6) && strstr("DELETE",op) )	return DELETE;
632 
633 	return UNKNOWN_OP;
634 }
635 
636 
637 /**
638 * load_schema -- sets up the internal representation of the schema.
639 */
load_schema(char * d)640 int load_schema(char* d)
641 {	int rc;
642 	char *tn;
643 	char line1 [MAX_ROW_SIZE];
644 	char line2 [MAX_ROW_SIZE];
645 	char fn [MAX_FILENAME_SIZE];
646 	tbl_cache_p tbc = NULL;
647 	table_p tp = NULL;
648 	FILE * fp = NULL;
649 	lnode_p h,n;
650 
651 	rc=0;
652 	h = n = NULL;
653 
654 	if(!d)
655 	{
656 		fprintf(stderr, "[load_schema]: null path to schema files.\n");
657 		return 1;
658 	}
659 
660 	tables = (tbl_cache_p)malloc(sizeof(tbl_cache_t));
661 	if(!tables)	return 1;
662 
663 	h = file_list(d, NULL);
664 
665 	while(h)
666 	{
667 		n = h->next;
668 
669 		/*create abs path to journal file (relative to db_home) */
670 		memset(fn, 0 , MAX_FILENAME_SIZE);
671 		strcat(fn, d);
672 		strcat(fn, "/");
673 		strcat(fn, h->p);
674 
675 		fp = fopen(fn, "r");
676 		if(!fp)
677 		{
678 			fprintf(stderr, "[load_schema]: FAILED to load schema file: %s.\n", h->p);
679 			break;
680 		}
681 
682 		tn = h->p;
683 		tbc = get_table(tn);
684 		if(!tbc)
685 		{
686 			fprintf(stderr, "[load_schema]: Table %s is not supported.\n",tn);
687 			fprintf(stderr, "[load_schema]: FAILED to load data for table: %s.\n", tn);
688 			goto done;
689 		}
690 
691 		tp = tbc->dtp;
692 
693 		while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL )
694 		{
695 			if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL )
696 			{
697 				if(strstr(line1, METADATA_COLUMNS))
698 				{
699 					if(0!=load_metadata_columns(tp, line2))
700 					{
701 						fprintf(stderr, "[load_schema]: FAILED to load METADATA COLS in table: %s.\n", tn);
702 						goto done;
703 					}
704 				}
705 
706 				if(strstr(line1, METADATA_KEY))
707 				{
708 					if(0!=load_metadata_key(tp, line2))
709 					{
710 						fprintf(stderr, "[load_schema]: FAILED to load METADATA KEYS in table: %s.\n", tn);
711 						goto done;
712 					}
713 				}
714 			}
715 			else
716 			{
717 				fprintf(stderr, "[load_schema]: FAILED to read schema value in table: %s.\n", tn);
718 				goto done;
719 			}
720 
721 		}
722 done:
723 		fclose(fp);
724 		h = n;
725 	}
726 
727 	while(h) /*free mem*/
728 	{	n = h->next;
729 		free(h->p);
730 		free(h);
731 		h = n;
732 	}
733 
734 	return rc;
735 }
736 
737 
738 
739 /**
740 * get_table -- return pointer to lazy initialized table struct
741 */
get_table(char * _s)742 tbl_cache_p get_table(char *_s)
743 {
744 	tbl_cache_p _tbc = tables;
745 	table_p _tp = NULL;
746 
747 	while(_tbc)
748 	{
749 		if(_tbc->dtp)
750 		{
751 			if(_tbc->dtp->name
752 			&& !strcmp(_tbc->dtp->name,_s))
753 			{
754 				return _tbc;
755 			}
756 		}
757 		_tbc = _tbc->next;
758 	}
759 
760 	_tbc = (tbl_cache_p)malloc(sizeof(tbl_cache_t));
761 	if(!_tbc)
762 		return NULL;
763 
764 	_tp = create_table(_s);
765 
766 	if(!_tp)
767 	{
768 		fprintf(stderr, "[get_table]: failed to create table.\n");
769 		free(_tbc);
770 		return NULL;
771 	}
772 
773 	_tbc->dtp = _tp;
774 
775 	if(tables)
776 		(tables)->prev = _tbc;
777 
778 	_tbc->next = tables;
779 	tables = _tbc;
780 
781 	return _tbc;
782 }
783 
784 
785 /**
786 * create_table -- returns an initialed table struct
787 */
create_table(char * _s)788 table_p create_table(char *_s)
789 {
790 	int i;
791 	table_p tp = NULL;
792 
793 	tp = (table_p)malloc(sizeof(table_t));
794 	if(!tp)	return NULL;
795 
796 	i=strlen(_s)+1;
797 	tp->name = (char*)malloc(i*sizeof(char));
798 	strncpy(tp->name, _s, i);
799 
800 	tp->ncols=0;
801 	tp->nkeys=0;
802 	tp->ro=0;
803 	tp->logflags=0;
804 	tp->db = NULL;
805 
806 	for(i=0;i<MAX_NUM_COLS;i++)
807 		tp->colp[i] = NULL;
808 
809 	return tp;
810 }
811 
812 
813 /**
814 * load_metadata_columns -- parses the METADATA_COLUMNS line into the internal
815 * 	representation.
816 */
load_metadata_columns(table_p _tp,char * line)817 int load_metadata_columns(table_p _tp, char* line)
818 {
819 	int n,len;
820 	char *s = NULL;
821 	char cn[64], ct[16];
822 	column_p col;
823 	n = len = 0;
824 
825 	if(!_tp) return -1;
826 	if(_tp->ncols!=0) return 0;
827 
828 	/* eg: line = "table_name(str) table_version(int)" */
829 	s = strtok(line, " \t");
830 	while(s!=NULL && n<MAX_NUM_COLS)
831 	{
832 		/* eg: meta[0]=table_name  meta[1]=str */
833 		sscanf(s,"%20[^(](%10[^)])[^\n]", cn, ct);
834 
835 		/* create column*/
836 		col = (column_p) malloc(sizeof(column_t));
837 		if(!col)
838 		{	fprintf(stderr, "load_metadata_columns: out of memory \n");
839 			return -1;
840 		}
841 
842 		/* set name*/
843 		len = strlen( cn )+1;
844 		col->name = (char*)malloc(len * sizeof(char));
845 		strcpy(col->name, cn );
846 
847 		/* set type*/
848 		len = strlen( ct )+1;
849 		col->type = (char*)malloc(len * sizeof(char));
850 		strcpy(col->type, ct );
851 
852 		_tp->colp[n] = col;
853 		n++;
854 		_tp->ncols++;
855 		s=strtok(NULL, " \t");
856 	}
857 
858 	return 0;
859 }
860 
861 
862 /**
863 * load_metadata_key -- parses the METADATA_KEY line into the internal
864 * 	representation.
865 */
load_metadata_key(table_p _tp,char * line)866 int load_metadata_key(table_p _tp, char* line)
867 {
868 	int ret,n,ci;
869 	char *s = NULL;
870 	ret = n = ci = 0;
871 
872 	if(!_tp)return -1;
873 
874 	s = strtok(line, " \t");
875 	while(s!=NULL && n< _tp->ncols)
876 	{
877 		ret = sscanf(s,"%i", &ci);
878 		if(ret != 1) return -1;
879 		if( _tp->colp[ci] )
880 		{	_tp->colp[ci]->kflag = 1;
881 			_tp->nkeys++;
882 		}
883 		n++;
884 		s=strtok(NULL, " ");
885 	}
886 
887 	return 0;
888 }
889 
890 
891 /**
892 * get_db -- lazy initialized DB access
893 *	Its like this so we get new db files only for the tables that have
894 *	journal files.
895 *	The db file on disk will be named:
896 *		<tablename>.new
897 */
get_db(table_p tp)898 DB* get_db(table_p tp)
899 {
900 	int rc;
901 	DB* db;
902 	char dfn[MAX_FILENAME_SIZE];
903 
904 	if( !tp) 	return NULL;
905 	if( tp->db) 	return tp->db;
906 
907 	memset(dfn, 0, MAX_FILENAME_SIZE);
908 	if(db_home)
909 	{
910 		strcpy(dfn, db_home);
911 		strcat(dfn, "/");
912 	}
913 
914 	/*creation of DB follows*/
915 	strcat(dfn, tp->name);
916 
917 	if ((rc = db_create(&db, NULL, 0)) != 0)
918 	{
919 		fprintf(stderr, "[create_table]: error db_create for table: %s.\n",dfn);
920 		return NULL;
921 	}
922 
923 	if ((rc = db->open(db, NULL, dfn, NULL, DB_HASH, DB_CREATE, 0664)) != 0)
924 	{
925 		fprintf(stderr, "[create_table]: error opening %s.\n",dfn);
926 		fprintf(stderr, "[create_table]: error msg: %s.\n",db_strerror(rc));
927 		return NULL;
928 	}
929 	tp->db = db;
930 
931 	import_schema(tp);
932 
933 	return db;
934 }
935 
936 
937 /**
938 */
import_schema(table_p tp)939 int import_schema(table_p tp)
940 {
941 	int rc, len1, len2;
942 	char line1 [MAX_ROW_SIZE];
943 	char line2 [MAX_ROW_SIZE];
944 	char fn [MAX_FILENAME_SIZE];
945 	FILE * fp = NULL;
946 	rc = 0;
947 
948 	if(!schema_dir)
949 	{
950 		fprintf(stderr, "[import_schema]: null schema dir.\n");
951 		return 1;
952 	}
953 
954 	if(!tp)
955 	{
956 		fprintf(stderr, "[import_schema]: null table parameter.\n");
957 		return 1;
958 	}
959 
960 	/*create abs path to journal file (relative to db_home) */
961 	memset(fn, 0 , MAX_FILENAME_SIZE);
962 	strcat(fn, schema_dir);
963 	strcat(fn, "/");
964 	strcat(fn, tp->name);
965 
966 	fp = fopen(fn, "r");
967 	if(!fp)
968 	{
969 		fprintf(stderr, "[import_schema]: FAILED to open def schema file: %s.\n", fn);
970 		return 1;
971 	}
972 
973 	while ( fgets(line1 , MAX_ROW_SIZE, fp) != NULL )
974 	{
975 		if ( fgets(line2 , MAX_ROW_SIZE, fp) != NULL )
976 		{
977 			len1 = strlen(line1)-1;
978 			len2 = strlen(line2)-1;
979 			line1[len1] = 0;
980 			line2[len2] = 0;
981 
982 			if((rc = _insert(tp->db, line1, line2, len1, len2) )!=0)
983 			{
984 				fprintf(stderr, "[import_schema]: FAILED to write schema def into table: %s.\n", tp->name);
985 				goto done;
986 			}
987 		}
988 		else
989 		{
990 			fprintf(stderr, "[import_schema]: FAILED to read schema def value in table: %s.\n", tp->name);
991 			goto done;
992 		}
993 
994 	}
995 done:
996 	fclose(fp);
997 	return rc;
998 }
999 
1000 
1001 
1002 /**
1003 * cleanup -- frees memory; closes any files.
1004 */
cleanup(void)1005 void cleanup(void)
1006 {
1007 	//cleanup
1008 	while(tables)
1009 	{	int i;
1010 		tbl_cache_p n   = tables->next;
1011 		table_p     tp  = tables->dtp;
1012 		if(tp)
1013 		{
1014 			free(tp->name);
1015 			for(i=0;i< tp->ncols;i++)
1016 			{
1017 				free(tp->colp[i]->name);
1018 				free(tp->colp[i]->type);
1019 				free(tp->colp[i]);
1020 			}
1021 
1022 			if(tp->db)
1023 				tp->db->close(tp->db, 0);
1024 
1025 			free(tp);
1026 		}
1027 		free(tables);
1028 		tables = n;
1029 	}
1030 }
1031 
1032