1 /*
2  * db_berkeley module, portions of this code were templated using
3  * the dbtext and postgres modules.
4 
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  */
24 
25 /*! \file
26  * Berkeley DB : Library
27  *
28  * \ingroup database
29  */
30 
31 
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include "../../core/ut.h"
38 #include "../../core/mem/mem.h"
39 #include "../../core/dprint.h"
40 
41 #include "bdb_fld.h"
42 #include "bdb_lib.h"
43 
44 static bdb_params_p _bdb_parms = NULL;
45 
46 /**
47  *
48  */
bdblib_init(bdb_params_p _p)49 int bdblib_init(bdb_params_p _p)
50 {
51 	bdb_params_p dp = NULL;
52 	if(_bdb_parms != NULL)
53 		return 0;
54 
55 	/*create default parms*/
56 	dp = (bdb_params_p)pkg_malloc(sizeof(bdb_params_t));
57 	if(dp == NULL) {
58 		ERR("not enough private memory\n");
59 		return -1;
60 	}
61 
62 	if(_p != NULL) {
63 		dp->cache_size = _p->cache_size;
64 		dp->auto_reload = _p->auto_reload;
65 		dp->log_enable = _p->log_enable;
66 		dp->journal_roll_interval = _p->journal_roll_interval;
67 	} else {
68 		dp->cache_size = (4 * 1024 * 1024); //4Mb
69 		dp->auto_reload = 0;
70 		dp->log_enable = 0;
71 		dp->journal_roll_interval = 3600;
72 	}
73 
74 	_bdb_parms = dp;
75 	return 0;
76 }
77 
78 
79 /**
80  * close all DBs and then the DBENV; free all memory
81  */
bdblib_destroy(void)82 int bdblib_destroy(void)
83 {
84 	if(_bdb_parms)
85 		pkg_free(_bdb_parms);
86 	return 0;
87 }
88 
89 
90 /** closes the underlying Berkeley DB.
91   assumes the lib data-structures are already initialzed;
92   used to sync and reload the db file.
93 */
bdblib_close(bdb_db_p _db_p,str * dirpath)94 int bdblib_close(bdb_db_p _db_p, str *dirpath)
95 {
96 	int rc;
97 	bdb_tcache_p _tbc;
98 	DB *_db = NULL;
99 	DB_ENV *_env = NULL;
100 
101 	if(_db_p == NULL || dirpath == NULL)
102 		return -1;
103 
104 	rc = 0;
105 
106 	if(_db_p == NULL) {
107 		DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
108 		return 1; /*table not found*/
109 	}
110 
111 	_env = _db_p->dbenv;
112 	_tbc = _db_p->tables;
113 	DBG("ENV %.*s \n", _db_p->name.len, _db_p->name.s);
114 	if(dirpath->len == _db_p->name.len
115 			&& !strncasecmp(dirpath->s, _db_p->name.s, _db_p->name.len)) {
116 		//close the whole dbenv
117 		DBG("ENV %.*s \n", dirpath->len, dirpath->s);
118 		while(_tbc) {
119 			if(_tbc->dtp) {
120 				_db = _tbc->dtp->db;
121 				if(_db)
122 					rc = _db->close(_db, 0);
123 				if(rc != 0)
124 					ERR("error closing %s\n", _tbc->dtp->name.s);
125 				_tbc->dtp->db = NULL;
126 			}
127 			_tbc = _tbc->next;
128 		}
129 		_env->close(_env, 0);
130 		_db_p->dbenv = NULL;
131 		return 0;
132 	}
133 
134 	//close a particular db
135 	while(_tbc) {
136 		if(_tbc->dtp) {
137 			DBG("checking DB %.*s \n", _tbc->dtp->name.len, _tbc->dtp->name.s);
138 
139 			if(_tbc->dtp->name.len == dirpath->len
140 					&& !strncasecmp(
141 							   _tbc->dtp->name.s, dirpath->s, dirpath->len)) {
142 				DBG("DB %.*s \n", dirpath->len, dirpath->s);
143 				_db = _tbc->dtp->db;
144 				if(_db)
145 					rc = _db->close(_db, 0);
146 				if(rc != 0)
147 					ERR("error closing %s\n", _tbc->dtp->name.s);
148 				_tbc->dtp->db = NULL;
149 				return 0;
150 			}
151 		}
152 		_tbc = _tbc->next;
153 	}
154 	DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
155 	return 1; /*table not found*/
156 }
157 
158 /** opens the underlying Berkeley DB.
159   assumes the lib data-structures are already initialzed;
160   used to sync and reload the db file.
161 */
bdblib_reopen(bdb_db_p _db_p,str * dirpath)162 int bdblib_reopen(bdb_db_p _db_p, str *dirpath)
163 {
164 	int rc, flags;
165 	bdb_tcache_p _tbc;
166 	DB *_db = NULL;
167 	DB_ENV *_env = NULL;
168 	rc = flags = 0;
169 	_tbc = NULL;
170 
171 	if(_db_p == NULL || dirpath == NULL)
172 		return -1;
173 
174 
175 	if(_db_p) {
176 		DBG("bdb: DB not found %.*s \n", dirpath->len, dirpath->s);
177 		return 1; /*table not found*/
178 	}
179 
180 	_env = _db_p->dbenv;
181 	_tbc = _db_p->tables;
182 
183 	if(dirpath->len == _db_p->name.len
184 			&& !strncasecmp(dirpath->s, _db_p->name.s, _db_p->name.len)) {
185 		//open the whole dbenv
186 		DBG("-- bdblib_reopen ENV %.*s \n", dirpath->len, dirpath->s);
187 		if(!_db_p->dbenv) {
188 			rc = bdblib_create_dbenv(&_env, dirpath->s);
189 			_db_p->dbenv = _env;
190 		}
191 
192 		if(rc != 0)
193 			return rc;
194 
195 		_env = _db_p->dbenv;
196 		_tbc = _db_p->tables;
197 
198 		while(_tbc) {
199 			if(_tbc->dtp) {
200 				if(!_tbc->dtp->db) {
201 					if((rc = db_create(&_db, _env, 0)) != 0) {
202 						_env->err(_env, rc, "db_create");
203 						ERR("error in db_create, db error: %s.\n",
204 								db_strerror(rc));
205 						bdblib_recover(_tbc->dtp, rc);
206 					}
207 				}
208 
209 				if((rc = _db->open(_db, NULL, dirpath->s, NULL, DB_HASH,
210 							DB_CREATE, 0664))
211 						!= 0) {
212 					_db->dbenv->err(_env, rc, "DB->open: %s", dirpath->s);
213 					ERR("error in db_open: %s.\n", db_strerror(rc));
214 					bdblib_recover(_tbc->dtp, rc);
215 				}
216 
217 				_tbc->dtp->db = _db;
218 			}
219 			_tbc = _tbc->next;
220 		}
221 		_env->close(_env, 0);
222 		return rc;
223 	}
224 
225 	// open a particular db
226 	while(_tbc) {
227 		if(_tbc->dtp) {
228 			ERR("checking DB %.*s \n", _tbc->dtp->name.len, _tbc->dtp->name.s);
229 
230 			if(_tbc->dtp->name.len == dirpath->len
231 					&& !strncasecmp(
232 							   _tbc->dtp->name.s, dirpath->s, dirpath->len)) {
233 				ERR("DB %.*s \n", dirpath->len, dirpath->s);
234 				if(!_tbc->dtp->db) {
235 					if((rc = db_create(&_db, _env, 0)) != 0) {
236 						_env->err(_env, rc, "db_create");
237 						ERR("error in db_create, db error: %s.\n",
238 								db_strerror(rc));
239 						bdblib_recover(_tbc->dtp, rc);
240 					}
241 				}
242 
243 				if((rc = _db->open(_db, NULL, dirpath->s, NULL, DB_HASH,
244 							DB_CREATE, 0664))
245 						!= 0) {
246 					_db->dbenv->err(_env, rc, "DB->open: %s", dirpath->s);
247 					ERR("bdb open: %s.\n", db_strerror(rc));
248 					bdblib_recover(_tbc->dtp, rc);
249 				}
250 				_tbc->dtp->db = _db;
251 				return rc;
252 			}
253 		}
254 		_tbc = _tbc->next;
255 	}
256 
257 	DBG("DB not found %.*s \n", dirpath->len, dirpath->s);
258 	return 1; /*table not found*/
259 }
260 
261 
262 /**
263  *
264  */
bdblib_create_dbenv(DB_ENV ** _dbenv,char * _home)265 int bdblib_create_dbenv(DB_ENV **_dbenv, char *_home)
266 {
267 	DB_ENV *env;
268 	char *progname;
269 	int rc, flags;
270 
271 	progname = "kamailio";
272 
273 	/* Create an environment and initialize it for additional error * reporting. */
274 	if((rc = db_env_create(&env, 0)) != 0) {
275 		ERR("db_env_create failed! bdb error: %s.\n", db_strerror(rc));
276 		return (rc);
277 	}
278 
279 	env->set_errpfx(env, progname);
280 
281 	/*  Specify the shared memory buffer pool cachesize */
282 	if((rc = env->set_cachesize(env, 0, _bdb_parms->cache_size, 0)) != 0) {
283 		ERR("dbenv set_cachsize failed! bdb error: %s.\n", db_strerror(rc));
284 		env->err(env, rc, "set_cachesize");
285 		goto err;
286 	}
287 
288 	/* Concurrent Data Store flags */
289 	flags = DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL | DB_THREAD;
290 
291 	/* Transaction Data Store flags ; not supported yet */
292 	/*
293 	flags = DB_CREATE |
294 		DB_RECOVER |
295 		DB_INIT_LOG |
296 		DB_INIT_LOCK |
297 		DB_INIT_MPOOL |
298 		DB_THREAD |
299 		DB_INIT_TXN;
300 	*/
301 
302 	/* Open the environment */
303 	if((rc = env->open(env, _home, flags, 0)) != 0) {
304 		ERR("dbenv is not initialized! bdb error: %s.\n", db_strerror(rc));
305 		env->err(env, rc, "environment open: %s", _home);
306 		goto err;
307 	}
308 
309 	*_dbenv = env;
310 	return (0);
311 
312 err:
313 	(void)env->close(env, 0);
314 	return (rc);
315 }
316 
317 
318 /**
319  */
bdblib_get_db(str * dirpath)320 bdb_db_p bdblib_get_db(str *dirpath)
321 {
322 	int rc;
323 	bdb_db_p _db_p = NULL;
324 
325 	if(dirpath == 0 || dirpath->s == NULL || dirpath->s[0] == '\0')
326 		return NULL;
327 
328 	if(_bdb_parms == NULL) {
329 		ERR("bdb: cache is not initialized! Check if you loaded bdb "
330 			"before any other module that uses it.\n");
331 		return NULL;
332 	}
333 
334 	if(!bdb_is_database(dirpath->s)) {
335 		ERR("bdb: database [%.*s] does not exists!\n", dirpath->len,
336 				dirpath->s);
337 		return NULL;
338 	}
339 
340 	_db_p = (bdb_db_p)pkg_malloc(sizeof(bdb_db_t));
341 	if(!_db_p) {
342 		ERR("no private memory for dbenv_t.\n");
343 		pkg_free(_db_p);
344 		return NULL;
345 	}
346 
347 	_db_p->name.s = (char *)pkg_malloc(dirpath->len * sizeof(char));
348 	memcpy(_db_p->name.s, dirpath->s, dirpath->len);
349 	_db_p->name.len = dirpath->len;
350 
351 	if((rc = bdblib_create_dbenv(&(_db_p->dbenv), dirpath->s)) != 0) {
352 		ERR("bdblib_create_dbenv failed");
353 		pkg_free(_db_p->name.s);
354 		pkg_free(_db_p);
355 		return NULL;
356 	}
357 
358 	_db_p->tables = NULL;
359 
360 	return _db_p;
361 }
362 
363 
364 /**
365  * look thru a linked list for the table. if dne, create a new one
366  * and add to the list
367 */
bdblib_get_table(bdb_db_t * _db,str * _s)368 bdb_tcache_p bdblib_get_table(bdb_db_t *_db, str *_s)
369 {
370 	bdb_tcache_p _tbc = NULL;
371 	bdb_table_p _tp = NULL;
372 
373 	if(!_db || !_s || !_s->s || _s->len <= 0)
374 		return NULL;
375 
376 	if(!_db->dbenv) {
377 		return NULL;
378 	}
379 
380 	_tbc = _db->tables;
381 	while(_tbc) {
382 		if(_tbc->dtp) {
383 
384 			if(_tbc->dtp->name.len == _s->len
385 					&& !strncasecmp(_tbc->dtp->name.s, _s->s, _s->len)) {
386 				return _tbc;
387 			}
388 		}
389 		_tbc = _tbc->next;
390 	}
391 
392 	_tbc = (bdb_tcache_p)pkg_malloc(sizeof(bdb_tcache_t));
393 	if(!_tbc)
394 		return NULL;
395 
396 	_tp = bdblib_create_table(_db, _s);
397 
398 	if(!_tp) {
399 		ERR("failed to create table.\n");
400 		pkg_free(_tbc);
401 		return NULL;
402 	}
403 
404 	_tbc->dtp = _tp;
405 
406 	if(_db->tables)
407 		(_db->tables)->prev = _tbc;
408 
409 	_tbc->next = _db->tables;
410 	_db->tables = _tbc;
411 
412 	return _tbc;
413 }
414 
415 
bdblib_log(int op,bdb_db_p _db_p,bdb_table_p _tp,char * _msg,int len)416 void bdblib_log(int op, bdb_db_p _db_p, bdb_table_p _tp, char *_msg, int len)
417 {
418 	if(!_tp || !len)
419 		return;
420 	if(!_bdb_parms->log_enable)
421 		return;
422 	if(_tp->logflags == JLOG_NONE)
423 		return;
424 
425 	if((_tp->logflags & op) == op) {
426 		int op_len = 7;
427 		char buf[MAX_ROW_SIZE + op_len];
428 		char *c;
429 		time_t now = time(NULL);
430 
431 		if(_bdb_parms->journal_roll_interval) {
432 			if((_tp->t)
433 					&& (now - _tp->t)
434 							   > _bdb_parms
435 										 ->journal_roll_interval) { /*try to roll logfile*/
436 				if(bdblib_create_journal(_db_p, _tp)) {
437 					ERR("Journaling has FAILED !\n");
438 					return;
439 				}
440 			}
441 		}
442 
443 		c = buf;
444 		switch(op) {
445 			case JLOG_INSERT:
446 				strncpy(c, "INSERT|", op_len);
447 				break;
448 			case JLOG_UPDATE:
449 				strncpy(c, "UPDATE|", op_len);
450 				break;
451 			case JLOG_DELETE:
452 				strncpy(c, "DELETE|", op_len);
453 				break;
454 		}
455 
456 		c += op_len;
457 		strncpy(c, _msg, len);
458 		c += len;
459 		*c = '\n';
460 		c++;
461 		*c = '\0';
462 
463 		if((_tp->logflags & JLOG_STDOUT) == JLOG_STDOUT)
464 			puts(buf);
465 
466 		if((_tp->logflags & JLOG_SYSLOG) == JLOG_SYSLOG)
467 			syslog(LOG_LOCAL6, "%s", buf);
468 
469 		if(_tp->fp) {
470 			if(!fputs(buf, _tp->fp))
471 				fflush(_tp->fp);
472 		}
473 	}
474 }
475 
476 /**
477  * The function is called to create a handle to a db table.
478  *
479  * On startup, we do not create any of the db handles.
480  * Instead it is done on first-use (lazy-initialized) to only create handles to
481  * files (db) that we require.
482  *
483  * There is one db file per kamailio table (eg. acc), and they should exist
484  * in your DB_PATH (refer to kamctlrc) directory.
485  *
486  * This function does _not_ create the underlying binary db tables.
487  * Creating the tables MUST be manually performed before
488  * kamailio startup by 'kamdbctl create'
489  *
490  * Function returns NULL on error, which will cause kamailio to exit.
491  *
492  */
bdblib_create_table(bdb_db_p _db,str * _s)493 bdb_table_p bdblib_create_table(bdb_db_p _db, str *_s)
494 {
495 
496 	int rc, i, flags;
497 	DB *bdb = NULL;
498 	bdb_table_p tp = NULL;
499 	char tblname[MAX_TABLENAME_SIZE];
500 
501 	if(!_db || !_db->dbenv) {
502 		ERR("no bdb_db_p or dbenv.\n");
503 		return NULL;
504 	}
505 
506 	tp = (bdb_table_p)pkg_malloc(sizeof(bdb_table_t));
507 	if(!tp) {
508 		ERR("no private memory for bdb_table_t.\n");
509 		return NULL;
510 	}
511 
512 	if((rc = db_create(&bdb, _db->dbenv, 0)) != 0) {
513 		_db->dbenv->err(_db->dbenv, rc, "database create");
514 		ERR("error in db_create, bdb error: %s.\n", db_strerror(rc));
515 		pkg_free(tp);
516 		return NULL;
517 	}
518 
519 	memset(tblname, 0, MAX_TABLENAME_SIZE);
520 	strncpy(tblname, _s->s, _s->len);
521 
522 	flags = DB_THREAD;
523 
524 	if((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664)) != 0) {
525 		_db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
526 		ERR("bdb open failed: %s.\n", db_strerror(rc));
527 		pkg_free(tp);
528 		return NULL;
529 	}
530 
531 	tp->name.s = (char *)pkg_malloc(_s->len * sizeof(char));
532 	memcpy(tp->name.s, _s->s, _s->len);
533 	tp->name.len = _s->len;
534 	tp->db = bdb;
535 	tp->ncols = 0;
536 	tp->nkeys = 0;
537 	tp->ro = 0;		  /*0=ReadWrite ; 1=ReadOnly*/
538 	tp->ino = 0;	  /*inode*/
539 	tp->logflags = 0; /*bitmap; 4=Delete, 2=Update, 1=Insert, 0=None*/
540 	tp->fp = 0;
541 	tp->t = 0;
542 
543 	for(i = 0; i < MAX_NUM_COLS; i++)
544 		tp->colp[i] = NULL;
545 
546 	/*load metadata; seeded\db_loaded when database are created*/
547 
548 	/*initialize columns with metadata*/
549 	rc = load_metadata_columns(tp);
550 	if(rc != 0) {
551 		ERR("FAILED to load METADATA COLS in table: %s.\n", tblname);
552 		goto error;
553 	}
554 
555 	/*initialize columns default values from metadata*/
556 	rc = load_metadata_defaults(tp);
557 	if(rc != 0) {
558 		ERR("FAILED to load METADATA DEFAULTS in table: %s.\n", tblname);
559 		goto error;
560 	}
561 
562 	rc = load_metadata_keys(tp);
563 	if(rc != 0) {
564 		ERR("FAILED to load METADATA KEYS in table: %s.\n", tblname);
565 		/*will have problems later figuring column types*/
566 		goto error;
567 	}
568 
569 	/*opened RW by default; Query to set the RO flag */
570 	rc = load_metadata_readonly(tp);
571 	if(rc != 0) {
572 		INFO("No METADATA_READONLY in table: %s.\n", tblname);
573 		/*non-critical; table will default to READWRITE*/
574 	}
575 
576 	if(tp->ro) {
577 		/*schema defines this table RO readonly*/
578 
579 		if((rc = bdb->close(bdb, 0)) != 0) {
580 			_db->dbenv->err(_db->dbenv, rc, "DB->close: %s", tblname);
581 			ERR("bdb close: %s.\n", db_strerror(rc));
582 			goto error;
583 		}
584 
585 		bdb = NULL;
586 		if((rc = db_create(&bdb, _db->dbenv, 0)) != 0) {
587 			_db->dbenv->err(_db->dbenv, rc, "database create");
588 			ERR("error in db_create.\n");
589 			goto error;
590 		}
591 
592 		flags = DB_THREAD | DB_RDONLY;
593 		if((rc = bdb->open(bdb, NULL, tblname, NULL, DB_HASH, flags, 0664))
594 				!= 0) {
595 			_db->dbenv->err(_db->dbenv, rc, "DB->open: %s", tblname);
596 			ERR("bdb open: %s.\n", db_strerror(rc));
597 			goto error;
598 		}
599 		tp->db = bdb;
600 	}
601 
602 	/* set the journaling flags; flags indicate which operations
603 	   need to be journalled. (e.g possible to only journal INSERT.)
604 	*/
605 	rc = load_metadata_logflags(tp);
606 	if(rc != 0)
607 		INFO("No METADATA_LOGFLAGS in table: %s.\n", tblname);
608 
609 	if((tp->logflags & JLOG_FILE) == JLOG_FILE)
610 		bdblib_create_journal(_db, tp);
611 
612 	return tp;
613 
614 error:
615 	if(tp) {
616 		pkg_free(tp->name.s);
617 		pkg_free(tp);
618 	}
619 	return NULL;
620 }
621 
bdblib_create_journal(bdb_db_p _db_p,bdb_table_p _tp)622 int bdblib_create_journal(bdb_db_p _db_p, bdb_table_p _tp)
623 {
624 	char *s;
625 	char fn[1024];
626 	char d[64];
627 	FILE *fp = NULL;
628 	struct tm *t;
629 	int bl;
630 	time_t tim = time(NULL);
631 
632 	if(!_db_p || !_tp)
633 		return -1;
634 	if(!_bdb_parms->log_enable)
635 		return 0;
636 	/* journal filename ; e.g. '/var/kamailio/db/location-YYYYMMDDhhmmss.jnl' */
637 	s = fn;
638 	strncpy(s, _db_p->name.s, _db_p->name.len);
639 	s += _db_p->name.len;
640 
641 	*s = '/';
642 	s++;
643 
644 	strncpy(s, _tp->name.s, _tp->name.len);
645 	s += _tp->name.len;
646 
647 	t = localtime(&tim);
648 	bl = strftime(d, 128, "-%Y%m%d%H%M%S.jnl", t);
649 	strncpy(s, d, bl);
650 	s += bl;
651 	*s = 0;
652 
653 	if(_tp->fp) { /* must be rolling. */
654 		if(fclose(_tp->fp)) {
655 			ERR("Failed to Close Log in table: %.*s .\n", _tp->name.len,
656 					_tp->name.s);
657 			return -1;
658 		}
659 	}
660 
661 	if((fp = fopen(fn, "w")) != NULL) {
662 		_tp->fp = fp;
663 	} else {
664 		ERR("Failed to Open Log in table: %.*s .\n", _tp->name.len,
665 				_tp->name.s);
666 		return -1;
667 	}
668 
669 	_tp->t = tim;
670 	return 0;
671 }
672 
load_metadata_columns(bdb_table_p _tp)673 int load_metadata_columns(bdb_table_p _tp)
674 {
675 	int ret, n, len;
676 	char dbuf[MAX_ROW_SIZE];
677 	char *s = NULL;
678 	char cn[64], ct[16];
679 	DB *db = NULL;
680 	DBT key, data;
681 	bdb_col_p col;
682 	ret = n = len = 0;
683 
684 	if(!_tp || !_tp->db)
685 		return -1;
686 
687 	if(_tp->ncols != 0)
688 		return 0;
689 
690 	db = _tp->db;
691 	memset(&key, 0, sizeof(DBT));
692 	memset(&data, 0, sizeof(DBT));
693 	memset(dbuf, 0, MAX_ROW_SIZE);
694 
695 	key.data = METADATA_COLUMNS;
696 	key.size = strlen(METADATA_COLUMNS);
697 
698 	/*memory for the result*/
699 	data.data = dbuf;
700 	data.ulen = MAX_ROW_SIZE;
701 	data.flags = DB_DBT_USERMEM;
702 
703 	if((ret = db->get(db, NULL, &key, &data, 0)) != 0) {
704 		db->err(db, ret, "load_metadata_columns DB->get failed");
705 		ERR("FAILED to find METADATA_COLUMNS in DB \n");
706 		return -1;
707 	}
708 
709 	/* eg: dbuf = "bdb_table_name(str) bdb_table_version(int)" */
710 	s = strtok(dbuf, " ");
711 	while(s != NULL && n < MAX_NUM_COLS) {
712 		/* eg: meta[0]=table_name  meta[1]=str */
713 		sscanf(s, "%20[^(](%10[^)])[^\n]", cn, ct);
714 
715 		/* create column*/
716 		col = (bdb_col_p)pkg_malloc(sizeof(bdb_col_t));
717 		if(!col) {
718 			ERR("out of private memory \n");
719 			return -1;
720 		}
721 
722 		/* set name*/
723 		len = strlen(cn);
724 		col->name.s = (char *)pkg_malloc(len * sizeof(char));
725 		memcpy(col->name.s, cn, len);
726 		col->name.len = len;
727 
728 		/*set column type*/
729 		if(strncmp(ct, "str", 3) == 0) {
730 			col->type = DB_STR;
731 		} else if(strncmp(ct, "int", 3) == 0) {
732 			col->type = DB_INT;
733 		} else if(strncmp(ct, "double", 6) == 0) {
734 			col->type = DB_DOUBLE;
735 		} else if(strncmp(ct, "datetime", 8) == 0) {
736 			col->type = DB_DATETIME;
737 		} else {
738 			col->type = DB_STR;
739 		}
740 
741 		col->flag = 0;
742 		_tp->colp[n] = col;
743 		n++;
744 		_tp->ncols++;
745 		s = strtok(NULL, " ");
746 	}
747 
748 	return 0;
749 }
750 
load_metadata_keys(bdb_table_p _tp)751 int load_metadata_keys(bdb_table_p _tp)
752 {
753 	int ret, n, ci;
754 	char dbuf[MAX_ROW_SIZE];
755 	char *s = NULL;
756 	DB *db = NULL;
757 	DBT key, data;
758 	ret = n = ci = 0;
759 
760 	if(!_tp || !_tp->db)
761 		return -1;
762 
763 	db = _tp->db;
764 	memset(&key, 0, sizeof(DBT));
765 	memset(&data, 0, sizeof(DBT));
766 	memset(dbuf, 0, MAX_ROW_SIZE);
767 	key.data = METADATA_KEY;
768 	key.size = strlen(METADATA_KEY);
769 	data.data = dbuf;
770 	data.ulen = MAX_ROW_SIZE;
771 	data.flags = DB_DBT_USERMEM;
772 
773 	if((ret = db->get(db, NULL, &key, &data, 0)) != 0) {
774 		db->err(db, ret, "load_metadata_keys DB->get failed");
775 		ERR("FAILED to find METADATA in table \n");
776 		return ret;
777 	}
778 
779 	s = strtok(dbuf, " ");
780 	while(s != NULL && n < _tp->ncols) {
781 		ret = sscanf(s, "%i", &ci);
782 		if(ret != 1)
783 			return -1;
784 		if(_tp->colp[ci]) {
785 			_tp->colp[ci]->flag = 1;
786 			_tp->nkeys++;
787 		}
788 		n++;
789 		s = strtok(NULL, " ");
790 	}
791 
792 	return 0;
793 }
794 
795 
load_metadata_readonly(bdb_table_p _tp)796 int load_metadata_readonly(bdb_table_p _tp)
797 {
798 	int i, ret;
799 	char dbuf[MAX_ROW_SIZE];
800 
801 	DB *db = NULL;
802 	DBT key, data;
803 	i = 0;
804 
805 	if(!_tp || !_tp->db)
806 		return -1;
807 
808 	db = _tp->db;
809 	memset(&key, 0, sizeof(DBT));
810 	memset(&data, 0, sizeof(DBT));
811 	memset(dbuf, 0, MAX_ROW_SIZE);
812 	key.data = METADATA_READONLY;
813 	key.size = strlen(METADATA_READONLY);
814 	data.data = dbuf;
815 	data.ulen = MAX_ROW_SIZE;
816 	data.flags = DB_DBT_USERMEM;
817 
818 	if((ret = db->get(db, NULL, &key, &data, 0)) != 0) {
819 		return ret;
820 	}
821 
822 	if(1 == sscanf(dbuf, "%i", &i))
823 		_tp->ro = (i > 0) ? 1 : 0;
824 
825 	return 0;
826 }
827 
load_metadata_logflags(bdb_table_p _tp)828 int load_metadata_logflags(bdb_table_p _tp)
829 {
830 	int i, ret;
831 	char dbuf[MAX_ROW_SIZE];
832 
833 	DB *db = NULL;
834 	DBT key, data;
835 	i = 0;
836 
837 	if(!_tp || !_tp->db)
838 		return -1;
839 
840 	db = _tp->db;
841 	memset(&key, 0, sizeof(DBT));
842 	memset(&data, 0, sizeof(DBT));
843 	memset(dbuf, 0, MAX_ROW_SIZE);
844 	key.data = METADATA_LOGFLAGS;
845 	key.size = strlen(METADATA_LOGFLAGS);
846 	data.data = dbuf;
847 	data.ulen = MAX_ROW_SIZE;
848 	data.flags = DB_DBT_USERMEM;
849 
850 	if((ret = db->get(db, NULL, &key, &data, 0)) != 0) {
851 		return ret;
852 	}
853 
854 	if(1 == sscanf(dbuf, "%i", &i))
855 		_tp->logflags = i;
856 
857 	return 0;
858 }
859 
load_metadata_defaults(bdb_table_p _tp)860 int load_metadata_defaults(bdb_table_p _tp)
861 {
862 	int ret, n, len;
863 	char dbuf[MAX_ROW_SIZE];
864 	char *s = NULL;
865 	char cv[64];
866 	DB *db = NULL;
867 	DBT key, data;
868 	bdb_col_p col;
869 	ret = n = len = 0;
870 
871 	if(!_tp || !_tp->db)
872 		return -1;
873 
874 	db = _tp->db;
875 	memset(&key, 0, sizeof(DBT));
876 	memset(&data, 0, sizeof(DBT));
877 	memset(dbuf, 0, MAX_ROW_SIZE);
878 
879 	key.data = METADATA_DEFAULTS;
880 	key.size = strlen(METADATA_DEFAULTS);
881 
882 	/*memory for the result*/
883 	data.data = dbuf;
884 	data.ulen = MAX_ROW_SIZE;
885 	data.flags = DB_DBT_USERMEM;
886 
887 	if((ret = db->get(db, NULL, &key, &data, 0)) != 0) {
888 		/*no defaults in DB; make some up.*/
889 		for(n = 0; n < _tp->ncols; n++) {
890 			col = _tp->colp[n];
891 			if(col) { /*set all columns default value to 'NULL' */
892 				len = strlen("NULL");
893 				col->dv.s = (char *)pkg_malloc(len * sizeof(char));
894 				memcpy(col->dv.s, "NULL", len);
895 				col->dv.len = len;
896 			}
897 		}
898 		return 0;
899 	}
900 
901 	/* use the defaults provided*/
902 	s = strtok(dbuf, DELIM);
903 	while(s != NULL && n < _tp->ncols) {
904 		ret = sscanf(s, "%s", cv);
905 		if(ret != 1)
906 			return -1;
907 		col = _tp->colp[n];
908 		if(col) { /*set column default*/
909 			len = strlen(s);
910 			col->dv.s = (char *)pkg_malloc(len * sizeof(char));
911 			memcpy(col->dv.s, cv, len);
912 			col->dv.len = len;
913 		}
914 		n++;
915 		s = strtok(NULL, DELIM);
916 	}
917 
918 	return 0;
919 }
920 
bdb_int2str(int _v,char * _s,int * _l)921 int bdb_int2str(int _v, char *_s, int *_l)
922 {
923 	int ret;
924 
925 	if((!_s) || (!_l) || (!*_l)) {
926 		ERR("Invalid parameter value\n");
927 		return -1;
928 	}
929 
930 	ret = snprintf(_s, *_l, "%-d", _v);
931 	if(ret < 0 || ret >= *_l) {
932 		ERR("Error in snprintf\n");
933 		return -1;
934 	}
935 	*_l = ret;
936 
937 	return 0;
938 }
939 
bdb_double2str(double _v,char * _s,int * _l)940 int bdb_double2str(double _v, char *_s, int *_l)
941 {
942 	int ret;
943 
944 	if((!_s) || (!_l) || (!*_l)) {
945 		ERR("Invalid parameter value\n");
946 		return -1;
947 	}
948 
949 	ret = snprintf(_s, *_l, "%-10.2f", _v);
950 	if(ret < 0 || ret >= *_l) {
951 		ERR("Error in snprintf\n");
952 		return -1;
953 	}
954 	*_l = ret;
955 
956 	return 0;
957 }
958 
bdb_time2str(time_t _v,char * _s,int * _l)959 int bdb_time2str(time_t _v, char *_s, int *_l)
960 {
961 	struct tm *t;
962 	int l;
963 
964 	if((!_s) || (!_l) || (*_l < 2)) {
965 		ERR("Invalid parameter value\n");
966 		return -1;
967 	}
968 
969 	*_s++ = '\'';
970 
971 	/* Convert time_t structure to format accepted by the database */
972 	t = localtime(&_v);
973 	l = strftime(_s, *_l - 1, "%Y-%m-%d %H:%M:%S", t);
974 
975 	if(l == 0) {
976 		ERR("Error during time conversion\n");
977 		/* the value of _s is now unspecified */
978 		_s = NULL;
979 		_l = 0;
980 		return -1;
981 	}
982 	*_l = l;
983 
984 	*(_s + l) = '\'';
985 	*_l = l + 2;
986 	return 0;
987 }
988 
989 /*
990  * Used when converting result from a query
991  */
bdb_val2str(db_fld_t * fld,char * sout,int * slen)992 int bdb_val2str(db_fld_t *fld, char *sout, int *slen)
993 {
994 	int l;
995 	db_fld_val_t *val;
996 
997 	if(fld->flags & DB_NULL) {
998 		*slen = snprintf(sout, *slen, "NULL");
999 		return 0;
1000 	}
1001 
1002 	val = &(fld->v);
1003 	switch(fld->type) {
1004 		case DB_INT:
1005 			if(bdb_int2str(val->int4, sout, slen) < 0) {
1006 				ERR("Error while converting int to string\n");
1007 				return -2;
1008 			} else {
1009 				DBG("Converted int to string\n");
1010 				return 0;
1011 			}
1012 			break;
1013 
1014 		case DB_BITMAP:
1015 			if(bdb_int2str(val->bitmap, sout, slen) < 0) {
1016 				ERR("Error while converting bitmap to string\n");
1017 				return -3;
1018 			} else {
1019 				DBG("Converted bitmap to string\n");
1020 				return 0;
1021 			}
1022 			break;
1023 
1024 		case DB_DOUBLE:
1025 			if(bdb_double2str(val->dbl, sout, slen) < 0) {
1026 				ERR("Error while converting double  to string\n");
1027 				return -3;
1028 			} else {
1029 				DBG("Converted double to string\n");
1030 				return 0;
1031 			}
1032 			break;
1033 
1034 		case DB_CSTR:
1035 			l = strlen(val->cstr);
1036 			if(*slen < l) {
1037 				ERR("Destination buffer too short for string\n");
1038 				return -4;
1039 			} else {
1040 				DBG("Converted string to string\n");
1041 				strncpy(sout, val->cstr, l);
1042 				sout[l] = 0;
1043 				*slen = l;
1044 				return 0;
1045 			}
1046 			break;
1047 
1048 		case DB_STR:
1049 			l = val->lstr.len;
1050 			if(*slen < l) {
1051 				ERR("Destination buffer too short for str\n");
1052 				return -5;
1053 			} else {
1054 				DBG("Converted str to string\n");
1055 				strncpy(sout, val->lstr.s, val->lstr.len);
1056 				*slen = val->lstr.len;
1057 				return 0;
1058 			}
1059 			break;
1060 
1061 		case DB_DATETIME:
1062 			if(bdb_time2str(val->time, sout, slen) < 0) {
1063 				ERR("Error while converting time_t to string\n");
1064 				return -6;
1065 			} else {
1066 				DBG("Converted time_t to string\n");
1067 				return 0;
1068 			}
1069 			break;
1070 
1071 		case DB_BLOB:
1072 			l = val->blob.len;
1073 			if(*slen < l) {
1074 				ERR("Destination buffer too short for blob\n");
1075 				return -7;
1076 			} else {
1077 				DBG("Converting BLOB [%s]\n", sout);
1078 				memcpy(sout, val->blob.s, val->blob.len);
1079 				*slen = l;
1080 				return 0;
1081 			}
1082 			break;
1083 
1084 		default:
1085 			ERR("Unknown data type\n");
1086 			return -8;
1087 	}
1088 }
1089 
1090 /*creates a composite key _k of length _klen from n values of _v;
1091   provide your own initialized memory for target _k and _klen;
1092   resulting value: _k = "KEY1 | KEY2"
1093   ko = key only
1094 */
1095 
bdblib_valtochar(bdb_table_p tp,db_fld_t * fld,int fld_count,char * kout,int * klen,int ktype)1096 int bdblib_valtochar(bdb_table_p tp, db_fld_t *fld, int fld_count, char *kout,
1097 		int *klen, int ktype)
1098 {
1099 	char *p;
1100 	static char sk[MAX_ROW_SIZE]; // subkey(sk) val
1101 	char *delim = DELIM;
1102 	char *cNULL = "NULL";
1103 	int len, total, sum;
1104 	int i, j, k;
1105 	bdb_fld_t *f;
1106 
1107 	p = kout;
1108 	len = sum = total = 0;
1109 	i = j = k = 0;
1110 
1111 	if(tp == NULL)
1112 		return -1;
1113 	if(fld == NULL || fld_count < 1)
1114 		return -1;
1115 	if(kout == NULL || klen == NULL)
1116 		return -1;
1117 	if(*klen < 1)
1118 		return -1;
1119 
1120 	memset(sk, 0, MAX_ROW_SIZE);
1121 	total = *klen;
1122 	*klen = 0; //sum
1123 
1124 	/*
1125 	  schema has specified keys
1126 	  verify all schema keys are provided
1127 	  use 'NULL' for those that are missing.
1128 	*/
1129 	for(i = 0; i < tp->ncols; i++) { /* i indexes columns in schema order */
1130 		if(ktype) {					 /* keymode; skip over non-key columns */
1131 			if(tp->colp[i]->flag == 0)
1132 				continue;
1133 		}
1134 
1135 		for(j = 0; j < fld_count; j++) {
1136 			f = DB_GET_PAYLOAD(fld + j);
1137 			/*
1138 			  j indexes the columns provided in _k
1139 			  which may be less than the total required by
1140 			  the schema. the app does not know the order
1141 			  of the columns in our schema!
1142 			 */
1143 			k = f->col_pos;
1144 
1145 			/*
1146 			 * k index will remap back to our schema order; like i
1147 			 */
1148 			if(i == k) {
1149 				/*
1150 				 KEY was provided; append to buffer;
1151 				 _k[j] contains a key, but its a key that
1152 				 corresponds to column k of our schema.
1153 				 now we know its a match, and we dont need
1154 				 index k for anything else
1155 				*/
1156 				len = total - sum;
1157 				if(bdb_val2str((fld + j), sk, &len) != 0) {
1158 					ERR("Destination buffer too short for subval %s\n", sk);
1159 					return -4;
1160 				}
1161 
1162 				sum += len;
1163 				if(sum > total) {
1164 					ERR("Destination buffer too short for subval %s\n", sk);
1165 					return -5;
1166 				}
1167 
1168 				strncpy(p, sk, len);
1169 				p += len;
1170 				*klen = sum;
1171 
1172 				sum += DELIM_LEN;
1173 				if(sum > total) {
1174 					ERR("Destination buffer too short for delim \n");
1175 					return -5;
1176 				}
1177 
1178 				/* append delim */
1179 				strncpy(p, delim, DELIM_LEN);
1180 				p += DELIM_LEN;
1181 				*klen = sum;
1182 
1183 
1184 				/* take us out of inner for loop
1185 				   and at the end of the outer loop
1186 				   to look for our next schema key
1187 				*/
1188 				goto next;
1189 			}
1190 		}
1191 
1192 		/*
1193 		 NO KEY provided; use the column default value (dv)
1194 		     i.e _tp->colp[i]->dv
1195 		*/
1196 		len = tp->colp[i]->dv.len;
1197 		sum += len;
1198 		if(sum > total) {
1199 			ERR("Destination buffer too short for subval %s\n", cNULL);
1200 			return -5;
1201 		}
1202 
1203 		strncpy(p, tp->colp[i]->dv.s, len);
1204 		p += len;
1205 		*klen = sum;
1206 
1207 		sum += DELIM_LEN;
1208 		if(sum > total) {
1209 			ERR("Destination buffer too short for delim \n");
1210 			return -5;
1211 		}
1212 
1213 		strncpy(p, delim, DELIM_LEN);
1214 		p += DELIM_LEN;
1215 		*klen = sum;
1216 	next:
1217 		continue;
1218 	}
1219 
1220 	return 0;
1221 }
1222 
1223 
1224 /**
1225  *
1226  */
bdb_db_free(bdb_db_p _dbp)1227 int bdb_db_free(bdb_db_p _dbp)
1228 {
1229 	bdb_tcache_p _tbc = NULL, _tbc0 = NULL;
1230 	if(!_dbp)
1231 		return -1;
1232 
1233 	_tbc = _dbp->tables;
1234 
1235 	while(_tbc) {
1236 		_tbc0 = _tbc->next;
1237 		bdb_tcache_free(_tbc);
1238 		_tbc = _tbc0;
1239 	}
1240 
1241 	if(_dbp->dbenv)
1242 		_dbp->dbenv->close(_dbp->dbenv, 0);
1243 
1244 	if(_dbp->name.s)
1245 		pkg_free(_dbp->name.s);
1246 
1247 	pkg_free(_dbp);
1248 
1249 	return 0;
1250 }
1251 
1252 
1253 /**
1254  *
1255  */
bdb_tcache_free(bdb_tcache_p _tbc)1256 int bdb_tcache_free(bdb_tcache_p _tbc)
1257 {
1258 	if(!_tbc)
1259 		return -1;
1260 
1261 	/*while ??!? */
1262 	if(_tbc->dtp)
1263 		bdb_table_free(_tbc->dtp);
1264 
1265 	pkg_free(_tbc);
1266 
1267 	return 0;
1268 }
1269 
1270 
1271 /**
1272  * close DB (sync data to disk) and free mem
1273  */
bdb_table_free(bdb_table_p _tp)1274 int bdb_table_free(bdb_table_p _tp)
1275 {
1276 	int i;
1277 	if(!_tp)
1278 		return -1;
1279 
1280 	if(_tp->db)
1281 		_tp->db->close(_tp->db, 0);
1282 
1283 	if(_tp->fp)
1284 		fclose(_tp->fp);
1285 
1286 	if(_tp->name.s)
1287 		pkg_free(_tp->name.s);
1288 
1289 	for(i = 0; i < _tp->ncols; i++) {
1290 		if(_tp->colp[i]) {
1291 			pkg_free(_tp->colp[i]->name.s);
1292 			pkg_free(_tp->colp[i]->dv.s);
1293 			pkg_free(_tp->colp[i]);
1294 		}
1295 	}
1296 
1297 	pkg_free(_tp);
1298 	return 0;
1299 }
1300 
bdblib_recover(bdb_table_p _tp,int _rc)1301 int bdblib_recover(bdb_table_p _tp, int _rc)
1302 {
1303 	switch(_rc) {
1304 		case DB_LOCK_DEADLOCK:
1305 			ERR("DB_LOCK_DEADLOCK detected !!\n");
1306 
1307 		case DB_RUNRECOVERY:
1308 			ERR("DB_RUNRECOVERY detected !! \n");
1309 			bdblib_destroy();
1310 			exit(1);
1311 			break;
1312 	}
1313 
1314 	return 0;
1315 }
1316 
1317 /**
1318  *
1319  */
bdb_is_database(char * dirpath)1320 int bdb_is_database(char *dirpath)
1321 {
1322 	DIR *dirp = NULL;
1323 
1324 	if(dirpath == NULL || dirpath[0] == '\0')
1325 		return 0;
1326 	dirp = opendir(dirpath);
1327 	if(dirp == NULL)
1328 		return 0;
1329 	closedir(dirp);
1330 
1331 	return 1;
1332 }
1333 
bdb_get_colpos(bdb_table_t * tp,char * name)1334 int bdb_get_colpos(bdb_table_t *tp, char *name)
1335 {
1336 	str s;
1337 	int i;
1338 
1339 	if(tp == NULL || name == NULL) {
1340 		ERR("bdb: bad parameters\n");
1341 		return -1;
1342 	}
1343 
1344 	s.s = name;
1345 	s.len = strlen(name);
1346 	for(i = 0; i < tp->ncols; i++) {
1347 		if(tp->colp[i]->name.len == s.len
1348 				&& !strncasecmp(s.s, tp->colp[i]->name.s, s.len))
1349 			return i;
1350 	}
1351 	return -1;
1352 }
1353 
bdb_str2time(char * s,time_t * v)1354 int bdb_str2time(char *s, time_t *v)
1355 {
1356 	struct tm time;
1357 
1358 	if((!s) || (!v)) {
1359 		ERR("bdb:invalid parameter value\n");
1360 		return -1;
1361 	}
1362 
1363 	memset(&time, '\0', sizeof(struct tm));
1364 	//if (strptime(s, "%Y-%m-%d %H:%M:%S", &time) == NULL) {
1365 	//	ERR("Error during time conversion\n");
1366 	//	return -1;
1367 	//}
1368 
1369 	time.tm_isdst = -1;
1370 	*v = mktime(&time);
1371 
1372 	return 0;
1373 }
1374 
bdb_str2double(char * s,double * v)1375 int bdb_str2double(char *s, double *v)
1376 {
1377 	if((!s) || (!v)) {
1378 		ERR("Invalid parameter value\n");
1379 		return -1;
1380 	}
1381 
1382 	*v = atof(s);
1383 	return 0;
1384 }
1385 
bdb_str2int(char * s,int * v)1386 int bdb_str2int(char *s, int *v)
1387 {
1388 	long tmp;
1389 
1390 	if(!s || !v) {
1391 		ERR("Invalid parameter value\n");
1392 		return -1;
1393 	}
1394 
1395 	tmp = strtoul(s, 0, 10);
1396 	if((tmp == ULONG_MAX && errno == ERANGE) || (tmp < INT_MIN)
1397 			|| (tmp > UINT_MAX)) {
1398 		ERR("Value out of range\n");
1399 		return -1;
1400 	}
1401 
1402 	*v = (int)tmp;
1403 	return 0;
1404 }
1405