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