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