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