Pārlūkot izejas kodu

mtree: in memory tree containers

- new module that loads db tables in tree structures in shared memory
- tree are construct based on allowed character list (default:
  0123456789)
- it returns associated value as string, and has support to get it in
  precompiled form, returning in AVPs (for now pairs of dstid:weight)
- there can be many trees defined, with mi commands to reload and print
  content
Daniel-Constantin Mierla 15 gadi atpakaļ
vecāks
revīzija
c9ba911f13

+ 16 - 0
modules/mtree/Makefile

@@ -0,0 +1,16 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=mtree.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+
+include ../../Makefile.modules

+ 240 - 0
modules/mtree/README

@@ -0,0 +1,240 @@
+mtree Module
+
+Daniel-Constantin Mierla
+
+   asipto.com
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2010 Daniel-Constantin Mierla (asipto.com)
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Exported Parameters
+
+              3.1. db_url (string)
+              3.2. mtree (string)
+              3.3. tprefix_column (string)
+              3.4. tvalue_column (string)
+              3.5. fetch_rows (integer)
+              3.6. char_list (string)
+              3.7. pv_value (string)
+
+        4. Exported Functions
+
+              4.1. mt_match(mtree, pv, mode)
+
+        5. Exported MI Functions
+
+              5.1. mt_list
+              5.2. mt_reload
+
+   List of Examples
+
+   1.1. Set db_url parameter
+   1.2. Set mtree parameter
+   1.3. Set prefix_column parameter
+   1.4. Set tvalue_column parameter
+   1.5. Set fetch_rows parameter
+   1.6. Set char_list parameter
+   1.7. Set pv_value parameter
+   1.8. mt_match usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. db_url (string)
+        3.2. mtree (string)
+        3.3. tprefix_column (string)
+        3.4. tvalue_column (string)
+        3.5. fetch_rows (integer)
+        3.6. char_list (string)
+        3.7. pv_value (string)
+
+   4. Exported Functions
+
+        4.1. mt_match(mtree, pv, mode)
+
+   5. Exported MI Functions
+
+        5.1. mt_list
+        5.2. mt_reload
+
+1. Overview
+
+   This module loads data indexed by prefixes from database and returns
+   associated string or precompiled value.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * A Kamailio database module (e.g., mysql).
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+3. Exported Parameters
+
+   3.1. db_url (string)
+   3.2. mtree (string)
+   3.3. tprefix_column (string)
+   3.4. tvalue_column (string)
+   3.5. fetch_rows (integer)
+   3.6. char_list (string)
+   3.7. pv_value (string)
+
+3.1. db_url (string)
+
+   URL of the database server to be used.
+
+   Default value is "mysql://openser:openserrw@localhost/openser".
+
+   Example 1.1. Set db_url parameter
+...
+modparam("mtree", "db_url", "dbdriver://username:password@dbhost/dbname")
+...
+
+3.2. mtree (string)
+
+   Definition of memory tree
+
+   Default value is "none".
+
+   Example 1.2. Set mtree parameter
+...
+modparam("mtree", "mtree", "name=mytable;dbtable=routes;type=0;")
+...
+
+3.3. tprefix_column (string)
+
+   Name of 'tprefix' column.
+
+   Default value is "tprefix".
+
+   Example 1.3. Set prefix_column parameter
+...
+modparam("mtree", "tprefix_column", "prefix")
+...
+
+3.4. tvalue_column (string)
+
+   Name of 'tvalue' column.
+
+   Default value is "tvalue".
+
+   Example 1.4. Set tvalue_column parameter
+...
+modparam("mtree", "tvalue_column", "ipaddr")
+...
+
+3.5. fetch_rows (integer)
+
+   Number of rows to be loaded in one step from database.
+
+   Default value is 1000.
+
+   Example 1.5. Set fetch_rows parameter
+...
+modparam("mtree", "fetch_rows", 4000)
+...
+
+3.6. char_list (string)
+
+   The list with characters allowed in prefix.
+
+   Default value is "0123456789".
+
+   Example 1.6. Set char_list parameter
+...
+modparam("mtree", "char_list", "0123456789*+")
+...
+
+3.7. pv_value (string)
+
+   The PV spec where to store the matched value. It can be any writtable
+   PV.
+
+   Default value is "$avp(s:tvalue)".
+
+   Example 1.7. Set pv_value parameter
+...
+modparam("mtree", "pv_value", "$var(mtval)")
+...
+
+4. Exported Functions
+
+   4.1. mt_match(mtree, pv, mode)
+
+4.1.  mt_match(mtree, pv, mode)
+
+   Match 'pv' value against mtree. mode is reserved for further use, now
+   set any integer or PV.
+
+   Example 1.8. mt_match usage
+...
+mt_match("mytree", "$rU", "0");
+...
+
+5. Exported MI Functions
+
+   5.1. mt_list
+   5.2. mt_reload
+
+5.1.  mt_list
+
+   List content of a tree.
+
+   Name: mt_list
+
+   Parameters:
+     * _mtree_ : name of tree to list.
+
+   MI FIFO Command Format:
+                :mt_list:_reply_fifo_file_
+                _mtname_
+                _empty_line_
+
+5.2.  mt_reload
+
+   Reload mtree from database.
+
+   Name: mt_mtree
+
+   Parameters:
+     * _mtname_
+       - name of mem tree
+
+   MI FIFO Command Format:
+                :mt_reload:_reply_fifo_file_
+                _mtname_
+                _empty_line_

+ 4 - 0
modules/mtree/doc/Makefile

@@ -0,0 +1,4 @@
+docs = mtree.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 36 - 0
modules/mtree/doc/mtree.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>mtree Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<affiliation><orgname>asipto.com</orgname></affiliation>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		    <email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2010</year>
+	    <holder>Daniel-Constantin Mierla (asipto.com)</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+	<xi:include href="mtree_admin.xml"/>
+    
+</book>

+ 274 - 0
modules/mtree/doc/mtree_admin.xml

@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+    
+    <title>&adminguide;</title>
+    
+    <section>
+	<title>Overview</title>
+	<para>
+		This module loads data indexed by prefixes from database and returns
+		associated string or precompiled value.
+	</para>
+    </section>
+    <section>
+	<title>Dependencies</title>
+	<section>
+	    <title>&kamailio; Modules</title>
+	    <para>
+		The following modules must be loaded before this module:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+			    <emphasis>A &kamailio; database module (e.g., mysql)</emphasis>.
+			</para>
+		    </listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+	<section>
+	    <title>External Libraries or Applications</title>
+	    <para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+			    <emphasis>None</emphasis>.
+			</para>
+		    </listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+    </section>
+    <section>
+	<title>Exported Parameters</title>
+	<section>
+	    <title><varname>db_url</varname> (string)</title>
+	    <para>
+		URL of the database server to be used.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>&defaultdb;</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>db_url</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "db_url", "&exampledb;")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>mtree</varname> (string)</title>
+	    <para>
+		Definition of memory tree
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>none</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>mtree</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "mtree", "name=mytable;dbtable=routes;type=0;")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>tprefix_column</varname> (string)</title>
+	    <para>
+		Name of 'tprefix' column.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>tprefix</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>prefix_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "tprefix_column", "prefix")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>tvalue_column</varname> (string)</title>
+	    <para>
+		Name of 'tvalue' column.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>tvalue</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>tvalue_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "tvalue_column", "ipaddr")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>fetch_rows</varname> (integer)</title>
+	    <para>
+		Number of rows to be loaded in one step from database.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is 1000.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>fetch_rows</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "fetch_rows", 4000)
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>char_list</varname> (string)</title>
+	    <para>
+		The list with characters allowed in prefix.
+		</para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>0123456789</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>char_list</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "char_list", "0123456789*+")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><varname>pv_value</varname> (string)</title>
+	    <para>
+		The PV spec where to store the matched value. It can be any
+		writtable PV.
+		</para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>$avp(s:tvalue)</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>pv_value</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("mtree", "pv_value", "$var(mtval)")
+...
+</programlisting>
+	    </example>
+	</section>
+
+	</section>
+	
+    <section>
+	<title>Exported Functions</title>
+ 	<section>
+	    <title>
+		<function moreinfo="none">mt_match(mtree, pv, mode)</function>
+	    </title>
+	    <para>
+		Match 'pv' value against mtree. mode is reserved for further
+		use, now set any integer or PV.
+	    </para>
+		<example>
+		<title><function>mt_match</function> usage</title>
+		<programlisting format="linespecific">
+...
+mt_match("mytree", "$rU", "0");
+...
+</programlisting>
+	    </example>
+	</section>
+	
+    </section>
+	
+	<section>
+		<title>Exported MI Functions</title>
+
+	<section>
+		<title>
+		<function moreinfo="none">mt_list</function>
+		</title>
+		<para>
+		List content of a tree.
+		</para>
+		<para>
+		Name: <emphasis>mt_list</emphasis>
+		</para>
+		<para>Parameters:</para>
+		<itemizedlist>
+			<listitem><para>_mtree_ : name of tree to list. </para></listitem>	  
+		</itemizedlist>
+		<para>
+		MI FIFO Command Format:
+		</para>
+        <programlisting  format="linespecific">
+		:mt_list:_reply_fifo_file_
+		_mtname_
+		_empty_line_
+		</programlisting>
+    </section>
+
+	<section>
+		<title>
+		<function moreinfo="none">mt_reload</function>
+		</title>
+		<para>
+		Reload mtree from database.
+		</para>
+		<para>
+		Name: <emphasis>mt_mtree</emphasis>
+		</para>
+		<para>Parameters:</para>
+		<itemizedlist>
+			<listitem><para>_mtname_</para> - name of mem tree</listitem>	  
+		</itemizedlist>
+		<para>
+		MI FIFO Command Format:
+		</para>
+        <programlisting  format="linespecific">
+		:mt_reload:_reply_fifo_file_
+		_mtname_
+		_empty_line_
+		</programlisting>
+    </section>
+    </section>
+
+</chapter>
+

+ 682 - 0
modules/mtree/mtree.c

@@ -0,0 +1,682 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file 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
+ *
+ *
+ * This file 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../mem/shm_mem.h"
+#include "../../parser/parse_param.h"
+#include "../../ut.h"
+#include "../../pvar.h"
+#include "../../lvalue.h"
+#include "../../shm_init.h"
+
+#include "mtree.h"
+
+//extern str mt_char_list = {"1234567890*",11};
+extern str mt_char_list;
+extern pv_spec_t pv_value;
+extern pv_spec_t pv_dstid;
+extern pv_spec_t pv_weight;
+extern int _mt_tree_type;
+
+/** structures containing prefix-value pairs */
+static m_tree_t **_ptree = NULL; 
+
+/* quick transaltion table */
+unsigned char _mt_char_table[256];
+
+void mt_char_table_init(void)
+{
+	unsigned int i;
+	for(i=0; i<=255; i++)
+		_mt_char_table[i] = 255;
+	for(i=0; i<mt_char_list.len; i++)
+		_mt_char_table[(unsigned int)mt_char_list.s[i]] = (unsigned char)i;
+}
+
+m_tree_t* mt_init_tree(str* tname, str *dbtable, int type)
+{
+	m_tree_t *pt = NULL;
+
+	pt = (m_tree_t*)shm_malloc(sizeof(m_tree_t));
+	if(pt==NULL)
+	{
+		LM_ERR("no more shm memory\n");
+		return NULL;
+	}
+	memset(pt, 0, sizeof(m_tree_t));
+
+	pt->type = type;
+	pt->tname.s = (char*)shm_malloc((1+tname->len)*sizeof(char));
+	if(pt->tname.s==NULL)
+	{
+		shm_free(pt);
+		LM_ERR("no more shm memory\n");
+		return NULL;
+	}
+	memset(pt->tname.s, 0, 1+tname->len);
+	memcpy(pt->tname.s, tname->s, tname->len);
+	pt->tname.len = tname->len;
+
+	pt->dbtable.s = (char*)shm_malloc((1+dbtable->len)*sizeof(char));
+	if(pt->dbtable.s==NULL)
+	{
+		shm_free(pt->tname.s);
+		shm_free(pt);
+		LM_ERR("no more shm memory\n");
+		return NULL;
+	}
+	memset(pt->dbtable.s, 0, 1+dbtable->len);
+	memcpy(pt->dbtable.s, dbtable->s, dbtable->len);
+	pt->dbtable.len = dbtable->len;
+	
+	return pt;
+}
+
+int mt_add_to_tree(m_tree_t *pt, str *sp, str *sv)
+{
+	int l;
+	mt_node_t *itn, *itn0;
+	
+	if(pt==NULL || sp==NULL || sp->s==NULL
+			|| sv==NULL || sv->s==NULL)
+	{
+		LM_ERR("bad parameters\n");
+		return -1;
+	}
+
+	if(sp->len>=MT_MAX_DEPTH)
+	{
+		LM_ERR("max prefix len exceeded\n");
+		return -1;
+	}
+	
+	l = 0;
+	if(pt->head == NULL)
+	{
+		pt->head = (mt_node_t*)shm_malloc(MT_NODE_SIZE*sizeof(mt_node_t));
+		if(pt->head == NULL)
+		{
+			LM_ERR("no more shm memory for tree head\n");
+			return -1;
+		}
+		memset(pt->head, 0, MT_NODE_SIZE*sizeof(mt_node_t));
+	}
+
+	itn0 = pt->head;
+	if(_mt_char_table[(unsigned int)sp->s[l]]==255)
+	{
+		LM_ERR("invalid char %d in prefix [%c (0x%x)]\n",
+				l, sp->s[l], sp->s[l]);
+		return -1;			
+	}
+	itn = itn0[_mt_char_table[(unsigned int)sp->s[l]]].child;
+
+	while(l < sp->len-1)
+	{
+		if(itn == NULL)
+		{
+			itn = (mt_node_t*)shm_malloc(MT_NODE_SIZE*sizeof(mt_node_t));
+			if(itn == NULL)
+			{
+				LM_ERR("no more shm mem\n");
+				return -1;
+			}
+			memset(itn, 0, MT_NODE_SIZE*sizeof(mt_node_t));
+			itn0[_mt_char_table[(unsigned int)sp->s[l]]].child = itn;
+		}
+		l++;	
+		if(_mt_char_table[(unsigned int)sp->s[l]]==255)
+		{
+			LM_ERR("invalid char %d in prefix [%c (0x%x)]\n",
+				l, sp->s[l], sp->s[l]);
+			return -1;			
+		}
+		itn0 = itn;
+		itn = itn0[_mt_char_table[(unsigned int)sp->s[l]]].child;
+	}
+
+	if(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s!=NULL)
+	{
+		LM_ERR("prefix already allocated [%.*s/%.*s] old: %.*s\n",
+			sp->len, sp->s, sv->len, sv->s,
+			itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.len,
+			itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s);
+		return -1;
+	}
+
+	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s
+			= (char*)shm_malloc((sv->len+1)*sizeof(char));
+	if(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s==NULL)
+	{
+		LM_ERR("no more shm mem!\n");
+		return -1;
+	}
+	strncpy(itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s, sv->s,
+			sv->len);
+	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.len = sv->len;
+	itn0[_mt_char_table[(unsigned int)sp->s[l]]].tvalue.s[sv->len] = '\0';
+	mt_node_set_payload(&itn0[_mt_char_table[(unsigned int)sp->s[l]]],
+			pt->type);
+	
+	return 0;
+}
+
+m_tree_t* mt_get_tree(str *tname)
+{
+	m_tree_t *it;
+	int ret;
+			   
+	if(_ptree==NULL || *_ptree==NULL)
+		return NULL;
+
+	if( tname==NULL || tname->s==NULL)
+	{
+		LM_ERR("bad parameters\n");
+		return NULL;
+	}
+
+	it = *_ptree;
+	/* search the tree for the asked tname */
+	while(it!=NULL)
+	{
+		ret = str_strcmp(&it->tname, tname);
+		if(ret>0)
+			return NULL;
+		if(ret==0)
+			return it;
+		it = it->next;
+	}
+
+	return it;
+}
+
+m_tree_t* mt_get_first_tree()
+{
+	if(_ptree==NULL || *_ptree==NULL)
+		return NULL;
+	return *_ptree;
+}
+
+
+str* mt_get_tvalue(m_tree_t *pt, str *tomatch, int *plen)
+{
+	int l, len;
+	mt_node_t *itn;
+	str *tvalue;
+
+	if(pt==NULL || tomatch==NULL || tomatch->s==NULL)
+	{
+		LM_ERR("bad parameters\n");
+		return NULL;
+	}
+	
+	l = len = 0;
+	itn = pt->head;
+	tvalue = NULL;
+
+	while(itn!=NULL && l < tomatch->len && l < MT_MAX_DEPTH)
+	{
+		/* check validity */
+		if(_mt_char_table[(unsigned int)tomatch->s[l]]==255)
+		{
+			LM_ERR("invalid char at %d in [%.*s]\n",
+					l, tomatch->len, tomatch->s);
+			return NULL;
+		}
+
+		if(itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue.s!=NULL)
+		{
+			tvalue = &itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue;
+			len = l+1;
+		}
+		
+		itn = itn[_mt_char_table[(unsigned int)tomatch->s[l]]].child;
+		l++;	
+	}
+	
+	if(plen!=NULL)
+		*plen = len;
+	
+	return tvalue;
+}
+
+int mt_match_prefix(struct sip_msg *msg, m_tree_t *it,
+		str *tomatch, int mode)
+{
+	int l, len, n;
+	int i, j;
+	mt_node_t *itn;
+	int ret;
+	str *tvalue;
+	int_str dstid_avp_name;
+	unsigned short dstid_name_type;
+	int_str weight_avp_name;
+	unsigned short weight_name_type;
+	int_str avp_value;
+	mt_dw_t *dw;
+	pv_value_t val;
+
+#define MT_MAX_DST_LIST	64
+	unsigned int tmp_list[2*(MT_MAX_DST_LIST+1)];
+
+	if(it==NULL || tomatch == NULL
+			|| tomatch->s == NULL)
+	{
+		LM_ERR("bad parameters\n");
+		return -1;
+	}
+
+	ret = 0;
+
+	l = len = 0;
+	n = 0;
+	if(it->type==MT_TREE_SVAL)
+	{
+		tvalue = mt_get_tvalue(it, tomatch, &len);
+		if(tvalue==NULL)
+		{
+			LM_DBG("no match for: %.*s\n", tomatch->len, tomatch->s);
+			return -1;
+		}
+		memset(&val, 0, sizeof(pv_value_t));
+		val.flags = PV_VAL_STR;
+		val.rs = *tvalue;
+		if(pv_value.setf(msg, &pv_value.pvp, (int)EQ_T, &val)<0)
+		{
+			LM_ERR("setting PV failed\n");
+			return -2;
+		}
+		return 0;
+	}
+
+	if(it->type!=MT_TREE_DW)
+		return -1; /* wrong tree type */
+
+	if(pv_get_avp_name(msg, &pv_dstid.pvp, &dstid_avp_name,
+				&dstid_name_type)<0)
+	{
+		LM_ERR("cannot get dstid avp name\n");
+		return -1;
+	}
+	if(pv_get_avp_name(msg, &pv_weight.pvp, &weight_avp_name,
+				&weight_name_type)<0)
+	{
+		LM_ERR("cannot get weight avp name\n");
+		return -1;
+	}
+
+	itn = it->head;
+	memset(tmp_list, 0, sizeof(unsigned int)*2*(MT_MAX_DST_LIST+1));
+
+	while(itn!=NULL && l < tomatch->len && l < MT_MAX_DEPTH)
+	{
+		/* check validity */
+		if(_mt_char_table[(unsigned int)tomatch->s[l]]==255)
+		{
+			LM_ERR("invalid char at %d in [%.*s]\n",
+					l, tomatch->len, tomatch->s);
+			return -1;
+		}
+
+		if(itn[_mt_char_table[(unsigned int)tomatch->s[l]]].tvalue.s!=NULL)
+		{
+			dw = (mt_dw_t*)itn[_mt_char_table[(unsigned int)tomatch->s[l]]].data;
+			while(dw) {
+				tmp_list[2*n]=dw->dstid;
+				tmp_list[2*n+1]=dw->weight;
+				n++;
+				if(n==MT_MAX_DST_LIST)
+					break;
+				dw = dw->next;
+			}
+			len = l+1;
+		}
+		if(n==MT_MAX_DST_LIST)
+			break;
+		
+		itn = itn[_mt_char_table[(unsigned int)tomatch->s[l]]].child;
+		l++;	
+	}
+	
+	if(n==0)
+		return -1; /* no match */
+	/* invalidate duplicated dstid, keeping longest match */
+	for(i=(n-1); i>0; i--)
+	{
+		if (tmp_list[2*i]!=0)
+		{
+			for(j=0; j<i; j++)
+				if(tmp_list[2*i]==tmp_list[2*j])
+					tmp_list[2*j] = 0;
+		}
+	}
+	/* sort the table -- bubble sort -- reverse order */
+	for (i = (n - 1); i >= 0; i--)
+	{
+		for (j = 1; j <= i; j++)
+		{
+			if (tmp_list[2*(j-1)+1] < tmp_list[2*j+1])
+			{
+				tmp_list[2*MT_MAX_DST_LIST]   = tmp_list[2*(j-1)];
+				tmp_list[2*MT_MAX_DST_LIST+1] = tmp_list[2*(j-1)+1];
+				tmp_list[2*(j-1)]   = tmp_list[2*j];
+				tmp_list[2*(j-1)+1] = tmp_list[2*j+1];
+				tmp_list[2*j]   = tmp_list[2*MT_MAX_DST_LIST];
+				tmp_list[2*j+1] = tmp_list[2*MT_MAX_DST_LIST+1];
+			}
+		}
+	}
+	/* add as avp */
+	for(i=0; i<n; i++)
+	{
+		if(tmp_list[2*i]!=0)
+		{
+			avp_value.n = (int)tmp_list[2*i+1];
+			add_avp(weight_name_type, weight_avp_name, avp_value);
+			avp_value.n = (int)tmp_list[2*i];
+			add_avp(dstid_name_type, dstid_avp_name, avp_value);
+		}
+	}
+	
+	return 0;
+}
+
+void mt_free_node(mt_node_t *pn, int type)
+{
+	int i;
+	if(pn==NULL)
+		return;
+
+	for(i=0; i<MT_NODE_SIZE; i++)
+	{
+		if(pn[i].tvalue.s!=NULL)
+		{
+			shm_free(pn[i].tvalue.s);
+			pn[i].tvalue.s   = NULL;
+			pn[i].tvalue.len = 0;
+			if(type==MT_TREE_DW)
+				mt_node_unset_payload(&pn[i], type);
+		}
+		if(pn[i].child!=NULL)
+		{
+			mt_free_node(pn[i].child, type);
+			pn[i].child = NULL;
+		}
+	}
+	shm_free(pn);
+	pn = NULL;
+	
+	return;
+}
+
+void mt_free_tree(m_tree_t *pt)
+{
+	if(pt == NULL)
+		return;
+
+	if(pt->head!=NULL) 
+		mt_free_node(pt->head, pt->type);
+	if(pt->next!=NULL)
+		mt_free_tree(pt->next);
+	if(pt->dbtable.s!=NULL)
+		shm_free(pt->dbtable.s);
+	if(pt->tname.s!=NULL)
+		shm_free(pt->tname.s);
+	
+	shm_free(pt);
+	pt = NULL;
+	return;
+}
+
+int mt_print_node(mt_node_t *pn, char *code, int len)
+{
+	int i;
+
+	if(pn==NULL || code==NULL || len>=MT_MAX_DEPTH)
+		return 0;
+	
+	for(i=0; i<MT_NODE_SIZE; i++)
+	{
+		code[len]=mt_char_list.s[i];
+		if(pn[i].tvalue.s!=NULL)
+			LM_DBG("[%.*s] [%.*s]\n",
+					len+1, code, pn[i].tvalue.len, pn[i].tvalue.s);
+		mt_print_node(pn[i].child, code, len+1);
+	}
+
+	return 0;
+}
+
+static char mt_code_buf[MT_MAX_DEPTH+1];
+int mt_print_tree(m_tree_t *pt)
+{
+	int len;
+
+	if(pt == NULL)
+	{
+		LM_DBG("tree is empty\n");
+		return 0;
+	}
+	
+	LM_DBG("[%.*s]\n", pt->tname.len, pt->tname.s);
+	len = 0;
+	mt_print_node(pt->head, mt_code_buf, len);
+	return mt_print_tree(pt->next);
+}
+
+int mt_node_set_payload(mt_node_t *node, int type)
+{
+	param_t *list;
+	param_t *it;
+	param_hooks_t hooks;
+	str s;
+	mt_dw_t *dwl;
+	mt_dw_t *dw;
+
+	if(type!=MT_TREE_DW)
+		return 0;
+	s = node->tvalue;
+	if(s.s[s.len-1]==';')
+		s.len--;
+	if(parse_params(&s, CLASS_ANY, &hooks, &list)<0)
+	{
+		LM_ERR("cannot parse tvalue payload [%.*s]\n", s.len, s.s);
+		return -1;
+	}
+	dwl = NULL;
+	for(it=list; it; it=it->next)
+	{
+		dw = (mt_dw_t*)shm_malloc(sizeof(mt_dw_t));
+		if(dw==NULL)
+		{
+			LM_ERR("no more shm\n");
+			goto error;
+		}
+		memset(dw, 0, sizeof(mt_dw_t));
+		str2int(&it->name, &dw->dstid);
+		str2int(&it->body, &dw->weight);
+		dw->next = dwl;
+		dwl = dw;
+	}
+	node->data = (void*)dwl;
+	free_params(list);
+	return 0;
+error:
+	while(dwl)
+	{
+		dw=dwl;
+		dwl=dwl->next;
+		shm_free(dwl);
+	}
+	free_params(list);
+	return -1;
+}
+
+int mt_node_unset_payload(mt_node_t *node, int type)
+{
+	mt_dw_t *dwl;
+	mt_dw_t *dw;
+
+	if(type!=MT_TREE_DW)
+		return 0;
+	dwl = (mt_dw_t*)node->data;
+	while(dwl)
+	{
+		dw=dwl;
+		dwl=dwl->next;
+		shm_free(dw);
+	}
+	node->data = NULL;
+	return 0;
+}
+
+int mt_table_spec(char* val)
+{
+	param_t* params_list = NULL;
+	param_hooks_t phooks;
+	param_t *pit=NULL;
+	if(val==NULL)
+		return -1;
+	m_tree_t tmp;
+	m_tree_t *it, *prev, *ndl;
+	str s;
+
+	if(!shm_initialized())
+	{
+		LM_ERR("shm not intialized - cannot define mtree now\n");
+		return 0;
+	}
+
+	s.s = val;
+	s.len = strlen(s.s);
+	if(s.s[s.len-1]==';')
+		s.len--;
+	if (parse_params(&s, CLASS_ANY, &phooks, &params_list)<0)
+		return -1;
+	memset(&tmp, 0, sizeof(m_tree_t));
+	for (pit = params_list; pit; pit=pit->next)
+	{
+		if (pit->name.len==4
+				&& strncasecmp(pit->name.s, "name", 4)==0) {
+			tmp.tname = pit->body;
+		} else if(pit->name.len==4
+				&& strncasecmp(pit->name.s, "type", 4)==0) {
+			str2sint(&pit->body, &tmp.type);
+		}  else if(pit->name.len==8
+				&& strncasecmp(pit->name.s, "dbtable", 7)==0) {
+			tmp.dbtable = pit->body;
+		}
+	}
+	if(tmp.tname.s==NULL)
+	{
+		LM_ERR("invalid mtree name\n");
+		free_params(params_list);
+		return -1;
+	}
+	if(tmp.dbtable.s==NULL)
+	{
+		LM_INFO("no table name - default mtree\n");
+		tmp.dbtable.s = "mtree";
+		tmp.dbtable.len = 5;
+	}
+	if(tmp.type!=1)
+		tmp.type = 0;
+	
+	/* check for same tree */
+	if(_ptree == 0)
+	{
+		/* tree list head in shm */
+		_ptree = (m_tree_t**)shm_malloc( sizeof(m_tree_t*) );
+		if (_ptree==0)
+		{
+			LM_ERR("out of shm mem for ptree\n");
+			goto error;
+		}
+		*_ptree=0;
+	}
+	it = *_ptree;
+	prev = NULL;
+	/* search the it position before which to insert new tvalue */
+	while(it!=NULL && str_strcmp(&it->tname, &tmp.tname)<0)
+	{	
+		prev = it;
+		it = it->next;
+	}
+
+	if(it!=NULL || str_strcmp(&it->tname, &tmp.tname)==0)
+	{
+		LM_ERR("duplicate tree with name [%s]\n", tmp.tname.s);
+		goto error; 
+	}
+	/* add new tname*/
+	if(it==NULL || str_strcmp(&it->tname, &tmp.tname)>0)
+	{
+		LM_DBG("adding new tname [%s]\n", tmp.tname.s);
+		
+		ndl = mt_init_tree(&tmp.tname, &tmp.dbtable, tmp.type);
+		if(ndl==NULL)
+		{
+			LM_ERR("no more shm memory\n");
+			goto error; 
+		}
+
+		ndl->next = it;
+		
+		/* new tvalue must be added as first element */
+		if(prev==NULL)
+			*_ptree = ndl;
+		else
+			prev->next=ndl;
+
+	}
+
+	free_params(params_list);
+	return 0;
+error:
+	free_params(params_list);
+	return -1;
+}
+
+void mt_destroy_trees(void)
+{
+	if (_ptree!=NULL)
+	{
+		if (*_ptree!=NULL)
+			mt_free_tree(*_ptree);
+		shm_free(_ptree);
+	}
+}
+
+int mt_defined_trees(void)
+{
+	if (_ptree!=NULL && *_ptree!=NULL)
+		return 1;
+	return 0;
+}
+

+ 87 - 0
modules/mtree/mtree.h

@@ -0,0 +1,87 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file 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
+ *
+ * This file 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+		       
+#ifndef _MTREE_H_
+#define _MTREE_H_
+
+#include "../../str.h"
+#include "../../parser/msg_parser.h"
+
+#define MT_TREE_SVAL	0	
+#define MT_TREE_DW		1
+
+typedef struct _mt_dw
+{
+	unsigned int dstid;
+	unsigned int weight;
+	struct _mt_dw *next;
+} mt_dw_t;
+
+typedef struct _mt_node
+{
+	str tvalue;
+	void *data;
+	struct _mt_node *child;
+} mt_node_t;
+
+#define MT_MAX_DEPTH	32
+
+#define MT_NODE_SIZE	mt_char_list.len
+
+typedef struct _m_tree
+{
+	str tname;
+	str dbtable;
+	int type;
+	mt_node_t *head;
+
+	struct _m_tree *next;
+} m_tree_t;
+
+
+/* prefix tree operations */
+int mt_add_to_tree(m_tree_t *pt, str *tprefix, str *tvalue);
+
+m_tree_t* mt_get_tree(str *tname);
+m_tree_t* mt_get_first_tree();
+
+str* mt_get_tvalue(m_tree_t *pt, str *tomatch, int *plen);
+int mt_match_prefix(struct sip_msg *msg, m_tree_t *pt,
+		str *tomatch, int mode);
+
+m_tree_t* mt_init_tree(str* tname, str* dbtable, int type);
+void mt_free_tree(m_tree_t *pt);
+int mt_print_tree(m_tree_t *pt);
+void mt_free_node(mt_node_t *pn, int type);
+
+void mt_char_table_init(void);
+int mt_node_set_payload(mt_node_t *node, int type);
+int mt_node_unset_payload(mt_node_t *node, int type);
+
+int mt_table_spec(char* val);
+void mt_destroy_trees(void);
+int mt_defined_trees(void);
+
+#endif
+

+ 722 - 0
modules/mtree/mtree_mod.c

@@ -0,0 +1,722 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "../../lib/srdb1/db_op.h"
+#include "../../lib/kmi/mi.h"
+#include "../../sr_module.h"
+#include "../../lib/srdb1/db.h"
+#include "../../mem/shm_mem.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../parser/parse_uri.h"
+#include "../../timer.h"
+#include "../../ut.h"
+#include "../../locking.h"
+#include "../../action.h"
+#include "../../mod_fix.h"
+#include "../../parser/parse_from.h"
+
+#include "mtree.h"
+
+MODULE_VERSION
+
+
+#define NR_KEYS			3
+
+int mt_fetch_rows = 1000;
+
+/** database connection */
+static db1_con_t *db_con = NULL;
+static db_func_t mt_dbf;
+
+#if 0
+INSERT INTO version (table_name, table_version) values ('mtree','1');
+CREATE TABLE mtree (
+    id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
+    tprefix VARCHAR(32) NOT NULL,
+    tvalue VARCHAR(128) DEFAULT '' NOT NULL,
+    CONSTRAINT tprefix_idx UNIQUE (tprefix)
+) ENGINE=MyISAM;
+#endif
+
+/** parameters */
+static str db_url = str_init(DEFAULT_DB_URL);
+static str tprefix_column = str_init("tprefix");
+static str tvalue_column  = str_init("tvalue");
+
+/* List of allowed chars for a prefix*/
+str mt_char_list = {"0123456789", 10};
+
+static str value_param = {"$avp(s:tvalue)", 0};
+static str dstid_param = {"$avp(s:tdstid)", 0};
+static str weight_param = {"$avp(s:tweight)", 0};
+static str count_param = {"$avp(s:tcount)", 0};
+pv_spec_t pv_value;
+pv_spec_t pv_dstid;
+pv_spec_t pv_weight;
+pv_spec_t pv_count;
+int _mt_tree_type = MT_TREE_SVAL;
+
+/* lock, ref counter and flag used for reloading the date */
+static gen_lock_t *mt_lock = 0;
+static volatile int mt_tree_refcnt = 0;
+static volatile int mt_reload_flag = 0;
+
+int mt_param(modparam_t type, void *val);
+static int fixup_mt_match(void** param, int param_no);
+static int w_mt_match(struct sip_msg* msg, char* str1, char* str2,
+		char* str3);
+
+static int  mod_init(void);
+static void mod_destroy(void);
+static int  child_init(void);
+static int  mod_child_init(int r);
+
+static int mt_match(struct sip_msg *msg, gparam_t *dm, gparam_t *var,
+		gparam_t *mode);
+
+static struct mi_root* mt_mi_reload(struct mi_root*, void* param);
+static struct mi_root* mt_mi_list(struct mi_root*, void* param);
+
+static int mt_load_db();
+
+static cmd_export_t cmds[]={
+	{"mt_match", (cmd_function)w_mt_match, 3, fixup_mt_match,
+		0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{"mtree",          STR_PARAM|USE_FUNC_PARAM, (void*)mt_param},
+	{"db_url",         STR_PARAM, &db_url.s},
+	{"tprefix_column", STR_PARAM, &tprefix_column.s},
+	{"tvalue_column",  STR_PARAM, &tvalue_column.s},
+	{"char_list",      STR_PARAM, &mt_char_list.s},
+	{"fetch_rows",     INT_PARAM, &mt_fetch_rows},
+	{"pv_value",       STR_PARAM, &value_param.s},
+	{"pv_dstid",       STR_PARAM, &dstid_param.s},
+	{"pv_weight",      STR_PARAM, &weight_param.s},
+	{"pv_count",       STR_PARAM, &count_param.s},
+	{0, 0, 0}
+};
+
+static mi_export_t mi_cmds[] = {
+	{ "mt_reload",  mt_mi_reload,  0,  0,  child_init },
+	{ "mt_list",    mt_mi_list,    0,  0,  0 },
+	{ 0, 0, 0, 0, 0}
+};
+
+
+struct module_exports exports = {
+	"mtree",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	mi_cmds,        /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	mod_child_init  /* per child init function */
+};
+
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	m_tree_t *pt = NULL;
+
+	if(register_mi_mod(exports.name, mi_cmds)!=0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
+	db_url.len = strlen(db_url.s);
+	tprefix_column.len = strlen(tprefix_column.s);
+	tvalue_column.len = strlen(tvalue_column.s);
+
+	value_param.len = strlen(value_param.s);
+	dstid_param.len = strlen(dstid_param.s);
+	weight_param.len = strlen(weight_param.s);
+	count_param.len = strlen(count_param.s);
+
+	if(pv_parse_spec(&value_param, &pv_value)<00
+			|| !(pv_is_w(&pv_value)))
+	{
+		LM_ERR("cannot parse value pv or is read only\n");
+		return -1;
+	}
+
+	if(pv_parse_spec(&dstid_param, &pv_dstid)<0
+			|| pv_dstid.type!=PVT_AVP)
+	{
+		LM_ERR("cannot parse dstid avp\n");
+		return -1;
+	}
+
+	if(pv_parse_spec(&weight_param, &pv_weight)<0
+			|| pv_weight.type!=PVT_AVP)
+	{
+		LM_ERR("cannot parse dstid avp\n");
+		return -1;
+	}
+
+	if(pv_parse_spec(&count_param, &pv_count)<0
+			|| !(pv_is_w(&pv_weight)))
+	{
+		LM_ERR("cannot parse count pv or is read-only\n");
+		return -1;
+	}
+
+	if(mt_fetch_rows<=0)
+		mt_fetch_rows = 1000;
+
+	mt_char_list.len = strlen(mt_char_list.s);
+	if(mt_char_list.len<=0)
+	{
+		LM_ERR("invalid prefix char list\n");
+		return -1;
+	}
+	LM_INFO("mt_char_list=%s \n", mt_char_list.s);
+	mt_char_table_init();
+
+	/* binding to mysql module */
+	if(db_bind_mod(&db_url, &mt_dbf))
+	{
+		LM_ERR("database module not found\n");
+		return -1;
+	}
+
+	if (!DB_CAPABILITY(mt_dbf, DB_CAP_ALL))
+	{
+		LM_ERR("database module does not "
+		    "implement all functions needed by the module\n");
+		return -1;
+	}
+
+	/* open a connection with the database */
+	db_con = mt_dbf.init(&db_url);
+	if(db_con==NULL)
+	{
+		LM_ERR("failed to connect to the database\n");        
+		return -1;
+	}
+	
+	LM_DBG("database connection opened successfully\n");
+	
+	if ( (mt_lock=lock_alloc())==0) {
+		LM_CRIT("failed to alloc lock\n");
+		goto error1;
+	}
+	if (lock_init(mt_lock)==0 ) {
+		LM_CRIT("failed to init lock\n");
+		goto error1;
+	}
+	
+	if(!mt_defined_trees())
+	{
+		LM_ERR("no trees defined\n");	
+		goto error1;
+	}
+
+	pt = mt_get_first_tree();
+	
+	while(pt!=NULL)
+	{
+		/* loading all information from database */
+		if(mt_load_db(&pt->tname)!=0)
+		{
+			LM_ERR("cannot load info from database\n");	
+			goto error1;
+		}
+		pt = pt->next;
+	}
+		
+	mt_dbf.close(db_con);
+	db_con = 0;
+
+#if 0
+	mt_print_tree(mt_get_first_tree());
+#endif
+
+	/* success code */
+	return 0;
+
+error1:
+	if (mt_lock)
+	{
+		lock_destroy( mt_lock );
+		lock_dealloc( mt_lock );
+		mt_lock = 0;
+	}
+	mt_destroy_trees();
+
+	if(db_con!=NULL)
+	{
+		mt_dbf.close(db_con);
+		db_con = 0;
+	}
+	return -1;
+}
+
+
+static int child_init(void)
+{
+	db_con = mt_dbf.init(&db_url);
+	if(db_con==NULL)
+	{
+		LM_ERR("failed to connect to database\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* each child get a new connection to the database */
+static int mod_child_init(int r)
+{
+	if ( child_init()!=0 )
+		return -1;
+
+	LM_DBG("#%d: database connection opened successfully\n",r);
+
+	return 0;
+}
+
+
+static void mod_destroy(void)
+{
+	LM_DBG("cleaning up\n");
+	mt_destroy_trees();
+	if (db_con!=NULL && mt_dbf.close!=NULL)
+		mt_dbf.close(db_con);
+		/* destroy lock */
+	if (mt_lock)
+	{
+		lock_destroy( mt_lock );
+		lock_dealloc( mt_lock );
+		mt_lock = 0;
+	}
+
+}
+
+static int fixup_mt_match(void** param, int param_no)
+{
+    if(param_no==1 || param_no==2) {
+		return fixup_spve_null(param, 1);
+    }
+    if (param_no != 3)	{
+		LM_ERR("invalid parameter number %d\n", param_no);
+		return E_UNSPEC;
+    }
+    return fixup_igp_null(param, 1);
+}
+
+
+/* use tree tn, match var, by mode, output in avp params */
+static int mt_match(struct sip_msg *msg, gparam_t *tn, gparam_t *var,
+		gparam_t *mode)
+{
+	str tname;
+	str tomatch;
+	int mval;
+	m_tree_t *tr = NULL;
+	
+	if(msg==NULL)
+	{
+		LM_ERR("received null msg\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, tn, &tname)<0)
+	{
+		LM_ERR("cannot get the tree name\n");
+		return -1;
+	}
+	if(fixup_get_svalue(msg, var, &tomatch)<0)
+	{
+		LM_ERR("cannot get the match var\n");
+		return -1;
+	}
+	if(fixup_get_ivalue(msg, mode, &mval)<0)
+	{
+		LM_ERR("cannot get the mode\n");
+		return -1;
+	}
+	
+again:
+	lock_get( mt_lock );
+	if (mt_reload_flag) {
+		lock_release( mt_lock );
+		sleep_us(5);
+		goto again;
+	}
+	mt_tree_refcnt++;
+	lock_release( mt_lock );
+
+	tr = mt_get_tree(&tname);
+	if(tr==NULL)
+	{
+		/* no tree with such name*/
+		goto error;
+	}
+
+	if(mt_match_prefix(msg, tr, &tomatch, mval)<0)
+	{
+		LM_INFO("no prefix found in [%.*s] for [%.*s]\n",
+				tname.len, tname.s,
+				tomatch.len, tomatch.s);
+		goto error;
+	}
+	
+	lock_get( mt_lock );
+	mt_tree_refcnt--;
+	lock_release( mt_lock );
+	return 1;
+
+error:
+	lock_get( mt_lock );
+	mt_tree_refcnt--;
+	lock_release( mt_lock );
+	return -1;
+}
+
+static int w_mt_match(struct sip_msg* msg, char* str1, char* str2,
+		char* str3)
+{
+	return mt_match(msg, (gparam_t*)str1, (gparam_t*)str2, (gparam_t*)str3);
+}
+
+int mt_param(modparam_t type, void *val)
+{
+	if(val==NULL)
+		goto error;
+
+	return mt_table_spec((char*)val);
+error:
+	return -1;
+
+}
+
+static int mt_load_db(str *tname)
+{
+	db_key_t db_cols[3] = {&tprefix_column, &tvalue_column};
+	str tprefix, tvalue;
+	db1_res_t* db_res = NULL;
+	int i, ret;
+	m_tree_t new_tree; 
+	m_tree_t *old_tree = NULL; 
+	mt_node_t *bk_head = NULL; 
+
+	if(db_con==NULL)
+	{
+		LM_ERR("no db connection\n");
+		return -1;
+	}
+	LM_ERR("attempting to load [%.*s]\n", tname->len, tname->s);
+	old_tree = mt_get_tree(tname);
+	if(old_tree==NULL)
+	{
+		LM_ERR("tree definition not found [%.*s]\n", tname->len, tname->s);
+		return -1;
+	}
+	memcpy(&new_tree, old_tree, sizeof(m_tree_t));
+	new_tree.head = 0;
+	new_tree.next = 0;
+
+	if (mt_dbf.use_table(db_con, &old_tree->dbtable) < 0)
+	{
+		LM_ERR("failed to use_table\n");
+		return -1;
+	}
+
+	if (DB_CAPABILITY(mt_dbf, DB_CAP_FETCH)) {
+		if(mt_dbf.query(db_con, 0, 0, 0, db_cols, 0, 2, 0, 0) < 0)
+		{
+			LM_ERR("Error while querying db\n");
+			return -1;
+		}
+		if(mt_dbf.fetch_result(db_con, &db_res, mt_fetch_rows)<0)
+		{
+			LM_ERR("Error while fetching result\n");
+			if (db_res)
+				mt_dbf.free_result(db_con, db_res);
+			goto error;
+		} else {
+			if(RES_ROW_N(db_res)==0)
+			{
+				return 0;
+			}
+		}
+	} else {
+		if((ret=mt_dbf.query(db_con, NULL, NULL, NULL, db_cols,
+				0, 2, 0, &db_res))!=0
+			|| RES_ROW_N(db_res)<=0 )
+		{
+			mt_dbf.free_result(db_con, db_res);
+			if( ret==0)
+			{
+				return 0;
+			} else {
+				goto error;
+			}
+		}
+	}
+
+	do {
+		for(i=0; i<RES_ROW_N(db_res); i++)
+		{
+			/* check for NULL values ?!?! */
+			tprefix.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
+			tprefix.len = strlen(tprefix.s);
+			
+			tvalue.s = (char*)(RES_ROWS(db_res)[i].values[1].val.string_val);
+			tvalue.len = strlen(tvalue.s);
+
+			if(tprefix.s==NULL || tvalue.s==NULL
+					|| tprefix.len<=0 || tvalue.len<=0)
+			{
+				LM_ERR("Error - bad values in db\n");
+				continue;
+			}
+		
+			if(mt_add_to_tree(&new_tree, &tprefix, &tvalue)<0)
+			{
+				LM_ERR("Error adding info to tree\n");
+				goto error;
+			}
+	 	}
+		if (DB_CAPABILITY(mt_dbf, DB_CAP_FETCH)) {
+			if(mt_dbf.fetch_result(db_con, &db_res, mt_fetch_rows)<0) {
+				LM_ERR("Error while fetching!\n");
+				if (db_res)
+					mt_dbf.free_result(db_con, db_res);
+				goto error;
+			}
+		} else {
+			break;
+		}
+	}  while(RES_ROW_N(db_res)>0);
+	mt_dbf.free_result(db_con, db_res);
+
+
+	/* block all readers */
+	lock_get( mt_lock );
+	mt_reload_flag = 1;
+	lock_release( mt_lock );
+
+	while (mt_tree_refcnt) {
+		sleep_us(10);
+	}
+
+	bk_head = old_tree->head;
+	old_tree->head = new_tree.head;
+
+	mt_reload_flag = 0;
+
+	/* free old data */
+	if (bk_head!=NULL)
+		mt_free_node(bk_head, new_tree.type);
+
+	return 0;
+
+error:
+	mt_dbf.free_result(db_con, db_res);
+	if (new_tree.head!=NULL)
+		mt_free_node(new_tree.head, new_tree.type);
+	return -1;
+}
+
+/**************************** MI ***************************/
+
+/**
+ * "mt_reload" syntax :
+ * \n
+ */
+static struct mi_root* mt_mi_reload(struct mi_root *cmd_tree, void *param)
+{
+	str tname = {0, 0};
+	m_tree_t *pt;
+	struct mi_node* node = NULL;
+
+	if(!mt_defined_trees())
+	{
+		LM_ERR("empty tree list\n");
+		return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	}
+
+	/* read tree name */
+	node = cmd_tree->node.kids;
+	if(node != NULL)
+	{
+		tname = node->value;
+		if(tname.s == NULL || tname.len== 0)
+			return init_mi_tree( 404, "domain not found", 16);
+
+		if(*tname.s=='.') {
+			tname.s = 0;
+			tname.len = 0;
+		}
+	}
+
+	pt = mt_get_first_tree();
+	
+	while(pt!=NULL)
+	{
+		if(tname.s==NULL || 
+			(tname.s!=NULL && pt->tname.len>=tname.len && 
+			 strncmp(pt->tname.s, tname.s, tname.len)==0))
+		{
+			/* re-loading table from database */
+			if(mt_load_db(&pt->tname)!=0)
+			{
+				LM_ERR("cannot re-load info from database\n");	
+				goto error;
+			}
+		}
+		pt = pt->next;
+	}
+	
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+
+error:
+	return init_mi_tree( 500, "Failed to reload",16);
+}
+
+
+int mt_print_mi_node(m_tree_t *tree, mt_node_t *pt, struct mi_node* rpl,
+		char *code, int len)
+{
+	int i;
+	struct mi_node* node = NULL;
+	struct mi_attr* attr= NULL;
+
+	if(pt==NULL || len>=MT_MAX_DEPTH)
+		return 0;
+	
+	for(i=0; i<MT_NODE_SIZE; i++)
+	{
+		code[len]=mt_char_list.s[i];
+		if(pt[i].tvalue.s!=NULL)
+		{
+			node = add_mi_node_child(rpl, 0, "MT", 3, 0, 0);
+			if(node == NULL)
+				goto error;
+			attr = add_mi_attr(node, MI_DUP_VALUE, "TNAME", 5,
+					tree->tname.s, tree->tname.len);
+			if(attr == NULL)
+				goto error;
+			attr = add_mi_attr(node, MI_DUP_VALUE, "TPREFIX", 7,
+						code, len+1);
+			if(attr == NULL)
+				goto error;
+					
+			attr = add_mi_attr(node, MI_DUP_VALUE, "TVALUE", 6,
+						pt[i].tvalue.s, pt[i].tvalue.len);
+			if(attr == NULL)
+				goto error;
+		}
+		if(mt_print_mi_node(tree, pt[i].child, rpl, code, len+1)<0)
+			goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+/**
+ * "mt_list" syntax :
+ *    tname
+ *
+ * 	- '.' (dot) means NULL value and will match anything
+ */
+
+#define strpos(s,c) (strchr(s,c)-s)
+struct mi_root* mt_mi_list(struct mi_root* cmd_tree, void* param)
+{
+	str tname = {0, 0};
+	m_tree_t *pt;
+	struct mi_node* node = NULL;
+	struct mi_root* rpl_tree = NULL;
+	struct mi_node* rpl = NULL;
+	static char code_buf[MT_MAX_DEPTH+1];
+	int len;
+
+	if(!mt_defined_trees())
+	{
+		LM_ERR("empty tree list\n");
+		return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	}
+
+	/* read tree name */
+	node = cmd_tree->node.kids;
+	if(node != NULL)
+	{
+		tname = node->value;
+		if(tname.s == NULL || tname.len== 0)
+			return init_mi_tree( 404, "domain not found", 16);
+
+		if(*tname.s=='.') {
+			tname.s = 0;
+			tname.len = 0;
+		}
+	}
+
+	rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if(rpl_tree == NULL)
+		return 0;
+	rpl = &rpl_tree->node;
+
+	pt = mt_get_first_tree();
+	
+	while(pt!=NULL)
+	{
+		if(tname.s==NULL || 
+			(tname.s!=NULL && pt->tname.len>=tname.len && 
+			 strncmp(pt->tname.s, tname.s, tname.len)==0))
+		{
+			len = 0;
+			if(mt_print_mi_node(pt, pt->head, rpl, code_buf, len)<0)
+				goto error;
+		}
+		pt = pt->next;
+	}
+	
+	return rpl_tree;
+
+error:
+	free_mi_tree(rpl_tree);
+	return 0;
+}
+