mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
628 lines
12 KiB
628 lines
12 KiB
/*
|
|
* $Id: matrix.c 4978 2008-09-23 14:25:02Z henningw $
|
|
*
|
|
* Copyright (C) 2007 1&1 Internet AG
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../lib/kmi/mi.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../usr_avp.h"
|
|
#include "../../locking.h"
|
|
#include "../../error.h"
|
|
#include "../../ut.h"
|
|
#include "../../mod_fix.h"
|
|
|
|
#include "db_matrix.h"
|
|
|
|
MODULE_VERSION
|
|
|
|
|
|
|
|
|
|
#define MAXCOLS 1000
|
|
|
|
|
|
|
|
|
|
str matrix_db_url = str_init(DEFAULT_RODB_URL);
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Generic parameter that holds a string, an int or an pseudo-variable
|
|
* @todo replace this with gparam_t
|
|
*/
|
|
struct multiparam_t {
|
|
enum {
|
|
MP_INT,
|
|
MP_STR,
|
|
MP_AVP,
|
|
MP_PVE,
|
|
} type;
|
|
union {
|
|
int n;
|
|
str s;
|
|
struct {
|
|
unsigned short flags;
|
|
int_str name;
|
|
} a;
|
|
pv_elem_t *p;
|
|
} u;
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ---- fixup functions: */
|
|
static int matrix_fixup(void** param, int param_no);
|
|
|
|
/* ---- exported commands: */
|
|
static int lookup_matrix(struct sip_msg *msg, struct multiparam_t *_first, struct multiparam_t *_second, struct multiparam_t *_dstavp);
|
|
|
|
/* ---- module init functions: */
|
|
static int mod_init(void);
|
|
static int child_init(int rank);
|
|
static int mi_child_init(void);
|
|
static void mod_destroy(void);
|
|
|
|
/* --- fifo functions */
|
|
struct mi_root * mi_reload_matrix(struct mi_root* cmd, void* param); /* usage: kamctl fifo reload_matrix */
|
|
|
|
|
|
|
|
|
|
static cmd_export_t cmds[]={
|
|
{ "matrix", (cmd_function)lookup_matrix, 3, matrix_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
|
{ 0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
|
|
|
|
|
|
static param_export_t params[] = {
|
|
matrix_DB_URL
|
|
matrix_DB_TABLE
|
|
matrix_DB_COLS
|
|
{ 0, 0, 0}
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Exported MI functions */
|
|
static mi_export_t mi_cmds[] = {
|
|
{ "reload_matrix", mi_reload_matrix, MI_NO_INPUT_FLAG, 0, mi_child_init },
|
|
{ 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
|
|
|
|
|
|
struct module_exports exports= {
|
|
"matrix",
|
|
DEFAULT_DLFLAGS,
|
|
cmds,
|
|
params,
|
|
0,
|
|
mi_cmds,
|
|
0,
|
|
0,
|
|
mod_init,
|
|
0,
|
|
mod_destroy,
|
|
child_init
|
|
};
|
|
|
|
|
|
|
|
|
|
struct first_t {
|
|
struct first_t *next;
|
|
int id;
|
|
short int second_list[MAXCOLS+1];
|
|
};
|
|
|
|
|
|
|
|
|
|
struct matrix_t {
|
|
struct first_t *head;
|
|
};
|
|
|
|
|
|
|
|
|
|
static gen_lock_t *lock = NULL;
|
|
static struct matrix_t *matrix = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
|
* fixes the module functions' parameters if it is a phone number.
|
|
* supports string, pseudo-variables and AVPs.
|
|
*
|
|
* @param param the parameter
|
|
*
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
static int mp_fixup(void ** param) {
|
|
pv_spec_t avp_spec;
|
|
struct multiparam_t *mp;
|
|
str s;
|
|
|
|
mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
|
|
if (mp == NULL) {
|
|
LM_ERR("out of pkg memory\n");
|
|
return -1;
|
|
}
|
|
memset(mp, 0, sizeof(struct multiparam_t));
|
|
|
|
s.s = (char *)(*param);
|
|
s.len = strlen(s.s);
|
|
|
|
if (s.s[0]!='$') {
|
|
/* This is string */
|
|
mp->type=MP_STR;
|
|
mp->u.s=s;
|
|
}
|
|
else {
|
|
/* This is a pseudo-variable */
|
|
if (pv_parse_spec(&s, &avp_spec)==0) {
|
|
LM_ERR("pv_parse_spec failed for '%s'\n", (char *)(*param));
|
|
pkg_free(mp);
|
|
return -1;
|
|
}
|
|
if (avp_spec.type==PVT_AVP) {
|
|
/* This is an AVP - could be an id or name */
|
|
mp->type=MP_AVP;
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
|
|
LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
|
|
pkg_free(mp);
|
|
return -1;
|
|
}
|
|
} else {
|
|
mp->type=MP_PVE;
|
|
if(pv_parse_format(&s, &(mp->u.p))<0) {
|
|
LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param));
|
|
pkg_free(mp);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
*param = (void*)mp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* fixes the module functions' parameters in case of AVP names.
|
|
*
|
|
* @param param the parameter
|
|
*
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
static int avp_name_fixup(void ** param) {
|
|
pv_spec_t avp_spec;
|
|
struct multiparam_t *mp;
|
|
str s;
|
|
|
|
s.s = (char *)(*param);
|
|
s.len = strlen(s.s);
|
|
if (s.len <= 0) return -1;
|
|
if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
|
|
LM_ERR("Malformed or non AVP definition <%s>\n", (char *)(*param));
|
|
return -1;
|
|
}
|
|
|
|
mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
|
|
if (mp == NULL) {
|
|
LM_ERR("out of pkg memory\n");
|
|
return -1;
|
|
}
|
|
memset(mp, 0, sizeof(struct multiparam_t));
|
|
|
|
mp->type=MP_AVP;
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
|
|
LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
|
|
pkg_free(mp);
|
|
return -1;
|
|
}
|
|
|
|
*param = (void*)mp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int matrix_fixup(void** param, int param_no)
|
|
{
|
|
if (param_no == 1) {
|
|
/* source id */
|
|
if (mp_fixup(param) < 0) {
|
|
LM_ERR("cannot fixup parameter %d\n", param_no);
|
|
return -1;
|
|
}
|
|
}
|
|
else if (param_no == 2) {
|
|
/* destination id */
|
|
if (mp_fixup(param) < 0) {
|
|
LM_ERR("cannot fixup parameter %d\n", param_no);
|
|
return -1;
|
|
}
|
|
}
|
|
else if (param_no == 3) {
|
|
/* destination avp name */
|
|
if (avp_name_fixup(param) < 0) {
|
|
LM_ERR("cannot fixup parameter %d\n", param_no);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void matrix_clear(void)
|
|
{
|
|
struct first_t *srcitem;
|
|
if (matrix) {
|
|
while (matrix->head) {
|
|
srcitem = matrix->head;
|
|
matrix->head = srcitem->next;
|
|
shm_free(srcitem);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
static int matrix_insert(int first, short int second, int res)
|
|
{
|
|
struct first_t *srcitem;
|
|
int i;
|
|
|
|
if ((second<0) || (second>MAXCOLS)) {
|
|
LM_ERR("invalid second value %d\n", second);
|
|
return -1;
|
|
}
|
|
LM_DBG("searching for %d, %d\n", first, second);
|
|
if (matrix) {
|
|
srcitem = matrix->head;
|
|
while (srcitem) {
|
|
if (srcitem->id == first) {
|
|
srcitem->second_list[second] = res;
|
|
LM_DBG("inserted (%d, %d, %d)\n", first, second, res);
|
|
return 0;
|
|
}
|
|
srcitem = srcitem->next;
|
|
}
|
|
/* not found */
|
|
srcitem = shm_malloc(sizeof(struct first_t));
|
|
if (srcitem == NULL) {
|
|
LM_ERR("out of shared memory.\n");
|
|
return -1;
|
|
}
|
|
memset(srcitem, 0, sizeof(struct first_t));
|
|
|
|
/* Mark all new cells as empty */
|
|
for (i=0; i<=MAXCOLS; i++) srcitem->second_list[i] = -1;
|
|
|
|
srcitem->next = matrix->head;
|
|
srcitem->id = first;
|
|
srcitem->second_list[second] = res;
|
|
matrix->head = srcitem;
|
|
}
|
|
|
|
LM_DBG("inserted new row for (%d, %d, %d)\n", first, second, res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the res id if the matrix contains an entry for the given indices, -1 otherwise.
|
|
*/
|
|
static int internal_lookup(int first, short int second)
|
|
{
|
|
struct first_t *item;
|
|
|
|
if ((second<0) || (second>MAXCOLS)) {
|
|
LM_ERR("invalid second value %d\n", second);
|
|
return -1;
|
|
}
|
|
|
|
if (matrix) {
|
|
item = matrix->head;
|
|
while (item) {
|
|
if (item->id == first) {
|
|
return item->second_list[second];
|
|
}
|
|
item = item->next;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int lookup_matrix(struct sip_msg *msg, struct multiparam_t *_srctree, struct multiparam_t *_second, struct multiparam_t *_dstavp)
|
|
{
|
|
int first;
|
|
int second;
|
|
struct usr_avp *avp;
|
|
int_str avp_val;
|
|
|
|
switch (_srctree->type) {
|
|
case MP_INT:
|
|
first = _srctree->u.n;
|
|
break;
|
|
case MP_AVP:
|
|
avp = search_first_avp(_srctree->u.a.flags, _srctree->u.a.name, &avp_val, 0);
|
|
if (!avp) {
|
|
LM_ERR("cannot find srctree AVP\n");
|
|
return -1;
|
|
}
|
|
if ((avp->flags&AVP_VAL_STR)) {
|
|
LM_ERR("cannot process string value in srctree AVP\n");
|
|
return -1;
|
|
}
|
|
else first = avp_val.n;
|
|
break;
|
|
default:
|
|
LM_ERR("invalid srctree type\n");
|
|
return -1;
|
|
}
|
|
|
|
switch (_second->type) {
|
|
case MP_INT:
|
|
second = _second->u.n;
|
|
break;
|
|
case MP_AVP:
|
|
avp = search_first_avp(_second->u.a.flags, _second->u.a.name, &avp_val, 0);
|
|
if (!avp) {
|
|
LM_ERR("cannot find second_value AVP\n");
|
|
return -1;
|
|
}
|
|
if ((avp->flags&AVP_VAL_STR)) {
|
|
LM_ERR("cannot process string value in second_value AVP\n");
|
|
return -1;
|
|
}
|
|
else second = avp_val.n;
|
|
break;
|
|
default:
|
|
LM_ERR("invalid second_value type\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* critical section start: avoids dirty reads when updating d-tree */
|
|
lock_get(lock);
|
|
|
|
avp_val.n=internal_lookup(first, second);
|
|
|
|
/* critical section end */
|
|
lock_release(lock);
|
|
|
|
if (avp_val.n<0) {
|
|
LM_INFO("lookup failed\n");
|
|
return -1;
|
|
}
|
|
|
|
/* set avp ! */
|
|
if (add_avp(_dstavp->u.a.flags, _dstavp->u.a.name, avp_val)<0) {
|
|
LM_ERR("add AVP failed\n");
|
|
return -1;
|
|
}
|
|
LM_INFO("result from lookup: %d\n", avp_val.n);
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Rebuild matrix using database entries
|
|
* \return negative on failure, positive on success, indicating the number of matrix entries
|
|
*/
|
|
static int db_reload_matrix(void)
|
|
{
|
|
db_key_t columns[3] = { &matrix_first_col, &matrix_second_col, &matrix_res_col };
|
|
db1_res_t *res;
|
|
int i;
|
|
int n = 0;
|
|
|
|
if (matrix_dbf.use_table(matrix_dbh, &matrix_table) < 0) {
|
|
LM_ERR("cannot use table '%.*s'.\n", matrix_table.len, matrix_table.s);
|
|
return -1;
|
|
}
|
|
if (matrix_dbf.query(matrix_dbh, NULL, NULL, NULL, columns, 0, 3, NULL, &res) < 0) {
|
|
LM_ERR("error while executing query.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* critical section start: avoids dirty reads when updating d-tree */
|
|
lock_get(lock);
|
|
|
|
matrix_clear();
|
|
|
|
if (RES_COL_N(res) > 2) {
|
|
for(i = 0; i < RES_ROW_N(res); i++) {
|
|
if ((!RES_ROWS(res)[i].values[0].nul) && (!RES_ROWS(res)[i].values[1].nul)) {
|
|
if ((RES_ROWS(res)[i].values[0].type == DB1_INT) &&
|
|
(RES_ROWS(res)[i].values[1].type == DB1_INT) &&
|
|
(RES_ROWS(res)[i].values[2].type == DB1_INT)) {
|
|
matrix_insert(RES_ROWS(res)[i].values[0].val.int_val, RES_ROWS(res)[i].values[1].val.int_val, RES_ROWS(res)[i].values[2].val.int_val);
|
|
n++;
|
|
}
|
|
else {
|
|
LM_ERR("got invalid result type from query.\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* critical section end */
|
|
lock_release(lock);
|
|
|
|
matrix_dbf.free_result(matrix_dbh, res);
|
|
|
|
LM_INFO("loaded %d matrix entries.\n", n);
|
|
return n;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int init_shmlock(void)
|
|
{
|
|
lock = lock_alloc();
|
|
if (!lock) {
|
|
LM_CRIT("cannot allocate memory for lock.\n");
|
|
return -1;
|
|
}
|
|
if (lock_init(lock) == 0) {
|
|
LM_CRIT("cannot initialize lock.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void destroy_shmlock(void)
|
|
{
|
|
if (lock) {
|
|
lock_destroy(lock);
|
|
lock_dealloc((void *)lock);
|
|
lock = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
struct mi_root * mi_reload_matrix(struct mi_root* cmd, void* param)
|
|
{
|
|
struct mi_root * tmp = NULL;
|
|
if(db_reload_matrix() >= 0) {
|
|
tmp = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
|
|
} else {
|
|
tmp = init_mi_tree( 500, "cannot reload matrix", 24);
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int init_matrix(void)
|
|
{
|
|
matrix = shm_malloc(sizeof(struct matrix_t));
|
|
if (!matrix) {
|
|
LM_ERR("out of shared memory\n");
|
|
return -1;
|
|
}
|
|
memset(matrix, 0, sizeof(struct matrix_t));
|
|
if (db_reload_matrix() < 0) {
|
|
LM_ERR("cannot populate matrix\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void destroy_matrix(void)
|
|
{
|
|
if (matrix) {
|
|
matrix_clear();
|
|
shm_free(matrix);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
static int mod_init(void)
|
|
{
|
|
if(register_mi_mod(exports.name, mi_cmds)!=0)
|
|
{
|
|
LM_ERR("failed to register MI commands\n");
|
|
return -1;
|
|
}
|
|
|
|
if (init_shmlock() != 0) return -1;
|
|
if (matrix_db_init() != 0) return -1;
|
|
if (matrix_db_open() != 0) return -1;
|
|
if (init_matrix() != 0) return -1;
|
|
matrix_db_close();
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int child_init(int rank)
|
|
{
|
|
if(rank==PROC_INIT || rank==PROC_TCP_MAIN)
|
|
return 0;
|
|
if (matrix_db_open() != 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static int mi_child_init(void)
|
|
{
|
|
if (matrix_db_open() != 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mod_destroy(void)
|
|
{
|
|
destroy_matrix();
|
|
destroy_shmlock();
|
|
matrix_db_close();
|
|
}
|