src/modules/matrix/matrix.c
1fb1997b
 /*
  * 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.
  *
7fd28b94
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
1fb1997b
  */
 
 #include <string.h>
 
cf83221d
 #include "../../core/mem/shm_mem.h"
 #include "../../core/sr_module.h"
 #include "../../core/mem/mem.h"
 #include "../../core/usr_avp.h"
 #include "../../core/locking.h"
 #include "../../core/error.h"
 #include "../../core/ut.h"
 #include "../../core/mod_fix.h"
7fd28b94
 #include "../../core/rpc_lookup.h"
 
1fb1997b
 
 #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;
 };
 
 
 
7fd28b94
 int matrix_rpc_init(void);
1fb1997b
 
 /* ---- 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 void mod_destroy(void);
 
 
 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
7fd28b94
 		matrix_DB_TABLE
 		matrix_DB_COLS
 		{ 0, 0, 0}
1fb1997b
 };
 
 
 
 
741e1de8
 struct module_exports exports = {
 	"matrix",        /* module name */
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	cmds,            /* cmd (cfg function) exports */
 	params,          /* param exports */
 	0,               /* RPC method exports */
 	0,               /* pseudo-variables exports */
 	0,               /* response handling function */
 	mod_init,        /* module init function */
 	child_init,      /* per-child init function */
 	mod_destroy      /* module destroy function */
1fb1997b
 };
 
 
 
 
 struct first_t {
7fd28b94
 	struct first_t *next;
1fb1997b
 	int id;
 	short int second_list[MAXCOLS+1];
 };
 
 
 
 
 struct matrix_t {
7fd28b94
 	struct first_t *head;
1fb1997b
 };
 
 
 
 
 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));
7fd28b94
 
1fb1997b
 	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;
 	}
7fd28b94
 
1fb1997b
 	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));
7fd28b94
 
1fb1997b
 	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;
7fd28b94
 
1fb1997b
 	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;
618a148c
 				LM_DBG("inserted (%d, %d, %d)\n", first, second, res);
1fb1997b
 				return 0;
 			}
 			srcitem = srcitem->next;
 		}
 		/* not found */
 		srcitem = shm_malloc(sizeof(struct first_t));
 		if (srcitem == NULL) {
618a148c
 			LM_ERR("out of shared memory.\n");
1fb1997b
 			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;
 	}
 
618a148c
 	LM_DBG("inserted new row for (%d, %d, %d)\n", first, second, res);
1fb1997b
 	return 0;
 }
 
 
 
 
 /* Returns the res id if the matrix contains an entry for the given indices, -1 otherwise.
7fd28b94
 */
1fb1997b
 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) {
7fd28b94
 		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");
1fb1997b
 			return -1;
 	}
 
 	switch (_second->type) {
7fd28b94
 		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");
1fb1997b
 			return -1;
 	}
7fd28b94
 
1fb1997b
 
 	/* 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;
7fd28b94
 
1fb1997b
 	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);
 
618a148c
 	LM_INFO("loaded %d matrix entries.\n", n);
1fb1997b
 	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;
 	}
 }
 
 
 
7fd28b94
 static void matrix_rpc_reload(rpc_t* rpc, void* c)
 {
2e54c3dd
 	if (matrix_db_open() != 0) {
 		rpc->fault(c, 500, "Failed to connect to db");
 		return;
 	}
7fd28b94
 	if(db_reload_matrix() < 0) {
 		rpc->fault(c, 500, "Reload failed");
 	}
2e54c3dd
 	matrix_db_close();
7fd28b94
 }
1fb1997b
 
7fd28b94
 static const char *matrix_rpc_reload_doc[2] = {
 	"reload matrix records from database",
 	0
 };
1fb1997b
 
 
7fd28b94
 rpc_export_t matrix_rpc_cmds[] = {
 	{"matrix.reload", matrix_rpc_reload, matrix_rpc_reload_doc, 0},
 	{0, 0, 0, 0}
 };
 
 int matrix_rpc_init(void)
 {
 	if (rpc_register_array(matrix_rpc_cmds)!=0)
 	{
 		LM_ERR("failed to register RPC commands\n");
 		return -1;
 	}
 	return 0;
 
 }
 
1fb1997b
 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)
 {
7fd28b94
 	if(matrix_rpc_init()<0) {
 		LM_ERR("failed to init RPC commands");
 		return -1;
 	}
b26f8623
 
1fb1997b
 	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)
 {
24731c59
 	if(rank==PROC_INIT || rank==PROC_TCP_MAIN)
 		return 0;
1fb1997b
 	if (matrix_db_open() != 0) return -1;
 	return 0;
 }
 
 
 
 static void mod_destroy(void)
 {
 	destroy_matrix();
 	destroy_shmlock();
 	matrix_db_close();
 }