123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677 |
- /*
- * $Id$
- *
- * Copyright (C) 2012 1&1 Internet Development
- *
- * This file is part of Kamailio, a free SIP server.
- *
- * Kamailio is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * Kamailio is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * History:
- * --------
- * 2012-01 initial version (Anca Vamanu)
- *
- */
- #include <stdio.h>
- #include <string.h>
- #include <time.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <dirent.h>
- #include "../../mem/shm_mem.h"
- #include "../../mem/mem.h"
- #include "../../dprint.h"
- #include "../../hashes.h"
- #include "../../lock_ops.h"
- #include "dbcassa_table.h"
- #define DBCASSA_TABLE_SIZE 16
- typedef struct rw_lock {
- gen_lock_t lock;
- int reload_flag;
- int data_refcnt;
- } rw_lock_t;
- typedef struct _dbcassa_tbl_htable
- {
- rw_lock_t lock;
- dbcassa_table_p dtp;
- } dbcassa_tbl_htable_t, *dbcassa_tbl_htable_p;
- static dbcassa_tbl_htable_p dbcassa_tbl_htable = NULL;
- extern str dbcassa_schema_path;
- static char full_path_buf[_POSIX_PATH_MAX + 1];
- /**
- * Check if file modified from last read
- * -1 - error
- * 0 - no change
- * 1 - changed
- */
- int dbcassa_check_mtime(time_t *mt)
- {
- struct stat s;
- if(stat(full_path_buf, &s) == 0)
- {
- if((int)s.st_mtime > (int)*mt)
- {
- *mt = s.st_mtime;
- LM_DBG("[%s] was updated\n", full_path_buf);
- return 1;
- }
- } else {
- LM_DBG("stat failed on [%s]\n", full_path_buf);
- return -1;
- }
- return 0;
- }
- /*
- * Create new table structure
- *
- * */
- dbcassa_table_p dbcassa_table_new(const str *_tbname, const str *_dbname)
- {
- struct stat s;
- dbcassa_table_p dtp = NULL;
- int size;
- if(!_tbname || !_dbname) {
- LM_ERR("Invalid parameters\n");
- return 0;
- }
- size = sizeof(dbcassa_table_t)+_tbname->len+_dbname->len;
- dtp = (dbcassa_table_p)shm_malloc(size);
- if(!dtp) {
- LM_ERR("No more shared memory\n");
- return 0;
- }
- memset(dtp, 0, size);
- size = sizeof(dbcassa_table_t);
- dtp->name.s = (char*)dtp + size;
- memcpy(dtp->name.s, _tbname->s, _tbname->len);
- dtp->name.len = _tbname->len;
- size+= _tbname->len;
- dtp->dbname.s = (char*)dtp + size;
- memcpy(dtp->dbname.s, _dbname->s, _dbname->len);
- dtp->dbname.len = _dbname->len;
- if(stat(full_path_buf, &s) == 0) {
- dtp->mt = s.st_mtime;
- LM_DBG("mtime is %d\n", (int)s.st_mtime);
- }
- return dtp;
- }
- dbcassa_column_p dbcassa_column_new(char *_s, int _l)
- {
- dbcassa_column_p dcp;
- int size;
- size = sizeof(dbcassa_column_t) + _l+ 1;
- dcp = (dbcassa_column_p)shm_malloc(size);
- if(!dcp) {
- LM_ERR("No more shared memory\n");
- return 0;
- }
- memset(dcp, 0, size);
- dcp->name.s = (char*)dcp + sizeof(dbcassa_column_t);
- memcpy(dcp->name.s, _s, _l);
- dcp->name.len = _l;
- dcp->name.s[_l] = '\0';
- return dcp;
- }
- int dbcassa_column_free(dbcassa_column_p dcp)
- {
- if(!dcp)
- return -1;
- shm_free(dcp);
- return 0;
- }
- int dbcassa_table_free(dbcassa_table_p _dtp)
- {
- dbcassa_column_p _cp, _cp0;
-
- if(!_dtp)
- return -1;
- /* cols*/
- _cp = _dtp->cols;
- while(_cp) {
- _cp0=_cp;
- _cp=_cp->next;
- dbcassa_column_free(_cp0);
- }
- /* key */
- if(_dtp->key)
- shm_free(_dtp->key);
- if(_dtp->sec_key)
- shm_free(_dtp->sec_key);
- shm_free(_dtp);
- return 0;
- }
- /**
- * Load the table schema from file
- */
- dbcassa_table_p dbcassa_load_file(str* dbn, str* tbn)
- {
- #define KEY_MAX_LEN 10
- FILE *fin=NULL;
- char buf[4096];
- int c, crow, ccol, bp, max_auto;
- dbcassa_table_p dtp = 0;
- dbcassa_column_p colp= 0;
- dbcassa_column_p key[KEY_MAX_LEN];
- dbcassa_column_p sec_key[KEY_MAX_LEN];
- enum {DBCASSA_FLINE_ST, DBCASSA_NLINE_ST, DBCASSA_NLINE2_ST} state;
- memset(key, 0, KEY_MAX_LEN*sizeof(dbcassa_column_p));
- memset(sec_key, 0, KEY_MAX_LEN*sizeof(dbcassa_column_p));
- LM_DBG("loading file [%s]\n", full_path_buf);
- fin = fopen(full_path_buf, "rt");
- if(!fin) {
- LM_ERR("Failed to open file\n");
- return 0;
- }
- dtp = dbcassa_table_new(tbn, dbn);
- if(!dtp)
- goto done;
- state = DBCASSA_FLINE_ST;
- crow = ccol = -1;
- c = fgetc(fin);
- max_auto = 0;
- while(c!=EOF) {
- switch(state) {
- case DBCASSA_FLINE_ST:
- bp = 0;
- while(c==DBCASSA_DELIM_C)
- c = fgetc(fin);
- if(c==DBCASSA_DELIM_R && !dtp->cols)
- goto clean;
- if(c==DBCASSA_DELIM_R) {
- if(dtp->nrcols <= 0)
- goto clean;
-
- state = DBCASSA_NLINE_ST;
- c = fgetc(fin);
- break;
- }
- while(c!=DBCASSA_DELIM_C && c!='(' && c!=DBCASSA_DELIM_R) {
- if(c==EOF)
- goto clean;
- buf[bp++] = c;
- c = fgetc(fin);
- }
- colp = dbcassa_column_new(buf, bp);
- if(!colp)
- goto clean;
- LM_DBG("new col [%.*s]\n", bp, buf);
- while(c==DBCASSA_DELIM_C)
- c = fgetc(fin);
- if(c!='(')
- goto clean;
- c = fgetc(fin);
- while(c==DBCASSA_DELIM_C)
- c = fgetc(fin);
- switch(c) {
- case 's':
- case 'S':
- colp->type = DB1_STR;
- LM_DBG("column[%d] is STR!\n", ccol+1);
- break;
- case 'i':
- case 'I':
- colp->type = DB1_INT;
- LM_DBG("column[%d] is INT!\n", ccol+1);
- break;
- case 'd':
- case 'D':
- colp->type = DB1_DOUBLE;
- LM_DBG("column[%d] is DOUBLE!\n", ccol+1);
- break;
- case 't':
- case 'T':
- colp->type = DB1_DATETIME;
- LM_DBG("column[%d] is TIME! Timestamp col has name [%s]\n", ccol+1, colp->name.s);
- if(dtp->ts_col) {
- LM_ERR("You can have only one column with type timestamp\n");
- goto clean;
- }
- dtp->ts_col = colp;
- break;
- default:
- LM_DBG("wrong column type!\n");
- goto clean;
- }
- while(c!='\n' && c!=EOF && c!=')' && c!= ',') {
- if(colp->type == DB1_STR && (c=='i'|| c=='I')) {
- colp->type = DB1_STRING;
- LM_DBG("column[%d] is actually STRING!\n", ccol+1);
- }
- c = fgetc(fin);
- }
- if(c == ')') {
- //LM_DBG("c=%c!\n", c);
- colp->next = dtp->cols;
- dtp->cols = colp;
- dtp->nrcols++;
- c = fgetc(fin);
- }
- else
- goto clean;
- ccol++;
- break;
- case DBCASSA_NLINE_ST:
- case DBCASSA_NLINE2_ST:
- // unique key
- while(c==DBCASSA_DELIM_C)
- c = fgetc(fin);
- if(c == DBCASSA_DELIM_R) {
- state = DBCASSA_NLINE2_ST;
- c = fgetc(fin);
- break;
- }
- if(c == EOF)
- break;
- bp= 0;
- while(c!=DBCASSA_DELIM_C && c!=DBCASSA_DELIM_R)
- {
- if(c==EOF)
- break;
- buf[bp++] = c;
- c = fgetc(fin);
- }
- colp = dtp->cols;
- while(colp) {
- if(bp==colp->name.len && strncmp(colp->name.s, buf, bp)==0) {
- if(state == DBCASSA_NLINE_ST)
- key[dtp->key_len++] = colp;
- else
- sec_key[dtp->seckey_len++] = colp;
- break;
- }
- colp = colp->next;
- }
- if(!colp) {
- LM_ERR("Undefined column in key [%.*s]\n", bp, buf);
- goto clean;
- }
- break;
- }
- }
- /* copy the keys into the table */
- if(dtp->key_len) {
- dtp->key = (dbcassa_column_p*)
- shm_malloc(dtp->key_len*sizeof(dbcassa_column_p));
- if(!dtp->key) {
- LM_ERR("No more share memory\n");
- goto clean;
- }
- for(ccol = 0; ccol< dtp->key_len; ccol++) {
- dtp->key[ccol] = key[ccol];
- LM_DBG("col [%.*s] in primary key\n", key[ccol]->name.len, key[ccol]->name.s);
- }
- }
- if(dtp->seckey_len) {
- dtp->sec_key = (dbcassa_column_p*)
- shm_malloc(dtp->seckey_len*sizeof(dbcassa_column_p));
- if(!dtp->sec_key) {
- LM_ERR("No more share memory\n");
- goto clean;
- }
- for(ccol = 0; ccol< dtp->seckey_len; ccol++) {
- dtp->sec_key[ccol] = sec_key[ccol];
- LM_DBG("col [%.*s] in secondary key\n", sec_key[ccol]->name.len, sec_key[ccol]->name.s);
- }
- }
- done:
- if(fin)
- fclose(fin);
- return dtp;
- clean:
- if(fin)
- fclose(fin);
- if(dtp)
- dbcassa_table_free(dtp);
- return NULL;
- }
- #define ref_read_data(rw_lock) \
- do {\
- again:\
- lock_get( &rw_lock.lock ); \
- if (rw_lock.reload_flag) { \
- lock_release( &rw_lock.lock ); \
- usleep(5); \
- goto again; \
- } \
- rw_lock.data_refcnt++; \
- lock_release( &rw_lock.lock ); \
- } while(0)
- #define unref_read_data(rw_lock) \
- do {\
- lock_get( &rw_lock.lock ); \
- rw_lock.data_refcnt--; \
- lock_release( &rw_lock.lock ); \
- } while(0)
- #define ref_write_data(rw_lock)\
- do {\
- lock_get( &rw_lock.lock ); \
- rw_lock.reload_flag = 1; \
- lock_release( &rw_lock.lock ); \
- while (rw_lock.data_refcnt) \
- usleep(10); \
- } while(0)
- #define unref_write_data(rw_lock)\
- rw_lock.reload_flag = 0;
- /*
- * Search the table schema
- * */
- dbcassa_table_p dbcassa_db_search_table(int hashidx, int hash,
- const str* dbn, const str *tbn)
- {
- dbcassa_table_p tbc = NULL;
- ref_read_data(dbcassa_tbl_htable[hashidx].lock);
- tbc = dbcassa_tbl_htable[hashidx].dtp;
- while(tbc) {
- LM_DBG("found dbname=%.*s, table=%.*s\n", tbc->dbname.len, tbc->dbname.s, tbc->name.len, tbc->name.s);
- if(tbc->hash==hash && tbc->dbname.len == dbn->len
- && tbc->name.len == tbn->len
- && !strncasecmp(tbc->dbname.s, dbn->s, dbn->len)
- && !strncasecmp(tbc->name.s, tbn->s, tbn->len))
- return tbc;
- tbc = tbc->next;
- }
- unref_read_data(dbcassa_tbl_htable[hashidx].lock);
- return NULL;
- }
- /**
- * Get the table schema. If the file was updated, update the table schema.
- */
- dbcassa_table_p dbcassa_db_get_table(const str* dbn, const str *tbn)
- {
- dbcassa_table_p tbc = NULL, old_tbc= NULL, new_tbc= NULL, prev_tbc= NULL;
- int hash;
- int hashidx;
- int len;
- if(!dbn || !tbn ) {
- LM_ERR("invalid parameter");
- return NULL;
- }
- hash = core_hash(dbn, tbn, DBCASSA_TABLE_SIZE);
- hashidx = hash % DBCASSA_TABLE_SIZE;
- ref_read_data(dbcassa_tbl_htable[hashidx].lock);
- tbc = dbcassa_tbl_htable[hashidx].dtp;
- while(tbc) {
- LM_DBG("found dbname=%.*s, table=%.*s\n", tbc->dbname.len, tbc->dbname.s, tbc->name.len, tbc->name.s);
- if(tbc->hash==hash && tbc->dbname.len == dbn->len
- && tbc->name.len == tbn->len
- && !strncasecmp(tbc->dbname.s, dbn->s, dbn->len)
- && !strncasecmp(tbc->name.s, tbn->s, tbn->len)) {
- memcpy(full_path_buf + dbcassa_schema_path.len, dbn->s, dbn->len);
- len = dbcassa_schema_path.len + dbn->len;
- full_path_buf[len++] = '/';
- memcpy(full_path_buf + len, tbn->s, tbn->len);
- full_path_buf[len + tbn->len] = '\0';
- if(dbcassa_check_mtime(&tbc->mt) == 0)
- return tbc;
- old_tbc = tbc;
- break;
- }
- tbc = tbc->next;
- }
- unref_read_data(dbcassa_tbl_htable[hashidx].lock);
- if(!old_tbc)
- return NULL;
- /* the file has changed - load again the schema */
- new_tbc = dbcassa_load_file((str*)dbn, (str*)tbn);
- if(!new_tbc)
- {
- LM_ERR("could not load database from file [%.*s]\n", tbn->len, tbn->s);
- return NULL;
- }
- new_tbc->hash = hashidx;
- /* lock for write */
- ref_write_data(dbcassa_tbl_htable[hashidx].lock);
- tbc = dbcassa_tbl_htable[hashidx].dtp;
- while(tbc) {
- if(tbc == old_tbc)
- break;
- prev_tbc = tbc;
- tbc = tbc->next;
- }
- /* somebody else might have rewritten it in the mean time? just return the existing one */
- if(!tbc) {
- unref_write_data(dbcassa_tbl_htable[hashidx].lock);
- return dbcassa_db_search_table(hashidx, hash, dbn, tbn);
- }
- /* replace the table */
- new_tbc->next = old_tbc->next;
- if(prev_tbc)
- prev_tbc->next = new_tbc;
- else
- dbcassa_tbl_htable[hashidx].dtp = new_tbc;
- dbcassa_table_free(old_tbc);
- unref_write_data(dbcassa_tbl_htable[hashidx].lock);
- /* lock for read, search the table and return */
- return dbcassa_db_search_table(hashidx, hash, dbn, tbn);
- }
- /*
- * Read all table schemas at startup
- * */
- int dbcassa_read_table_schemas(void)
- {
- int i, j;
- str db_name, tb_name;
- DIR* srcdir = opendir(dbcassa_schema_path.s);
- DIR* db_dir;
- struct dirent* dent;
- int fn_len = dbcassa_schema_path.len;
- struct stat fstat;
- int dir_len;
- dbcassa_table_p tbc;
- unsigned int hashidx;
- /* init tables' hash table */
- if (!dbcassa_tbl_htable) {
- dbcassa_tbl_htable = (dbcassa_tbl_htable_p)shm_malloc(DBCASSA_TABLE_SIZE*
- sizeof(dbcassa_tbl_htable_t));
- if(dbcassa_tbl_htable==NULL)
- {
- LM_CRIT("no enough shm mem\n");
- return -1;
- }
- memset(dbcassa_tbl_htable, 0, DBCASSA_TABLE_SIZE*sizeof(dbcassa_tbl_htable_t));
- for(i=0; i<DBCASSA_TABLE_SIZE; i++)
- {
- if (lock_init(&dbcassa_tbl_htable[i].lock.lock)==0)
- {
- LM_CRIT("cannot init tables' sem's\n");
- for(j=i-1; j>=0; j--)
- lock_destroy(&dbcassa_tbl_htable[j].rw_lock.lock);
- return -1;
- }
- }
- }
- memset(full_path_buf, 0, _POSIX_PATH_MAX);
- strcpy(full_path_buf, dbcassa_schema_path.s);
- if (full_path_buf[dbcassa_schema_path.len - 1] != '/') {
- full_path_buf[fn_len++]= '/';
- dbcassa_schema_path.len++;
- }
- if (srcdir == NULL) {
- perror("opendir");
- return -1;
- }
- LM_DBG("Full name= %.*s\n", fn_len, full_path_buf);
- while((dent = readdir(srcdir)) != NULL)
- {
- if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
- continue;
- /* Calculate full name, check we are in file length limits */
- if ((fn_len + strlen(dent->d_name) + 1) > _POSIX_PATH_MAX)
- continue;
- db_name.s = dent->d_name;
- db_name.len = strlen(dent->d_name);
-
- strcpy(full_path_buf+fn_len, dent->d_name);
- dir_len = fn_len + db_name.len;
- LM_DBG("Full dir name= %.*s\n", dir_len, full_path_buf);
- if (stat(full_path_buf, &fstat) < 0) {
- LM_ERR("stat failed %s\n", strerror(errno));
- continue;
- }
- if (!S_ISDIR(fstat.st_mode)) {
- LM_ERR("not a directory\n");
- continue;
- }
- /*
- if (fstatat(dirfd(srcdir), dent->d_name, &st) < 0)
- {
- perror(dent->d_name);
- continue;
- }
- */
- LM_DBG("Found database %s\n", dent->d_name);
- db_dir = opendir(full_path_buf);
- if(!db_dir) {
- LM_ERR("Failed to open dictory %s\n", full_path_buf);
- continue;
- }
- full_path_buf[dir_len++]= '/';
- while((dent = readdir(db_dir)) != NULL)
- {
- if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
- continue;
- LM_DBG("database table %s\n", dent->d_name);
- if(dir_len + strlen(dent->d_name)+1 > _POSIX_PATH_MAX) {
- LM_ERR("File len too large\n");
- continue;
- }
- strcpy(full_path_buf+dir_len, dent->d_name);
- tb_name.s = dent->d_name;
- tb_name.len = strlen(dent->d_name);
- LM_DBG("File path= %s\n", full_path_buf);
- tbc = dbcassa_load_file(&db_name, &tb_name);
- if(!tbc)
- {
- LM_ERR("could not load database from file [%s]\n", tb_name.s);
- return -1;
- }
- hashidx = core_hash(&db_name, &tb_name, DBCASSA_TABLE_SIZE);
- tbc->hash = hashidx;
- tbc->next = dbcassa_tbl_htable[hashidx].dtp;
- dbcassa_tbl_htable[hashidx].dtp = tbc;
- }
- closedir(db_dir);
- }
- closedir(srcdir);
- return 0;
- }
- /*
- * Destroy table schema table at shutdown
- * */
- void dbcassa_destroy_htable(void)
- {
- int i;
- dbcassa_table_p tbc, tbc0;
- /* destroy tables' hash table*/
- if(dbcassa_tbl_htable==0)
- return;
- for(i=0; i<DBCASSA_TABLE_SIZE; i++) {
- lock_destroy(&dbcassa_tbl_htable[i].rw_lock.lock);
- tbc = dbcassa_tbl_htable[i].dtp;
- while(tbc) {
- tbc0 = tbc;
- tbc = tbc->next;
- dbcassa_table_free(tbc0);
- }
- }
- shm_free(dbcassa_tbl_htable);
- }
- void dbcassa_lock_release(dbcassa_table_p tbc)
- {
- unref_read_data(dbcassa_tbl_htable[tbc->hash].lock);
- }
|