Browse Source

Merge pull request #443 from linuxmaniac/vseva/cfgt

modules/cfgt: debugger config test support
Victor Seva 9 years ago
parent
commit
393385a097

+ 17 - 0
modules/cfgt/Makefile

@@ -0,0 +1,17 @@
+#
+# cfgt module makefile
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=cfgt.so
+LIBS=
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srutils/srutils
+
+include ../../Makefile.modules

+ 126 - 0
modules/cfgt/README

@@ -0,0 +1,126 @@
+cfgt Module
+
+Victor Seva
+
+   sipwise.com
+
+Edited by
+
+Victor Seva
+
+   <[email protected]>
+
+   Copyright © 2015 Victor Seva (sipwise.com)
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. basedir (string)
+              3.2. mask (int)
+              3.3. callid_prefix (string)
+
+   List of Examples
+
+   1.1. Set cfgtrace parameter
+   1.2. Set mask parameter
+   1.3. Set callid_prefix parameter
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. basedir (string)
+        3.2. mask (int)
+        3.3. callid_prefix (string)
+
+1. Overview
+
+   This module provides a report of the way Kamailio SIP Server Platform
+   configuration has been executed as part of a unit test for different
+   SIP scenarios.
+
+   In order to identify defferent scenarios a prefix string should be used
+   at Call-ID header.
+
+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:
+     * None.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+3. Parameters
+
+   3.1. basedir (string)
+   3.2. mask (int)
+   3.3. callid_prefix (string)
+
+3.1. basedir (string)
+
+   Control where the config reports should be stored. The dir must exists
+   and Kamailio SIP Server Platform must have perms to write on it.
+
+   Default value is "/tmp".
+
+   Example 1.1. Set cfgtrace parameter
+...
+modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest")
+...
+
+3.2. mask (int)
+
+   mask - Control the type of vars it should display in the report:
+     * 1 - dump null values
+     * 2 - dump avp vars
+     * 4 - dump script vars
+     * 8 - dump xavp vars
+     * 16 - dump DP_OTHER vars
+     * 32 - dump ALL vars
+
+   Default value is "32" (ALL).
+
+   Example 1.2. Set mask parameter
+...
+# dump xavp(8) and avp(4) vars
+modparam("cfgt", "mask", 12)
+...
+
+3.3. callid_prefix (string)
+
+   Prefix used to indentify test scenario messages. Last char of the
+   string will be used as delimiter.
+
+   Default value is "NGCP%" (using "%" as delimiter).
+
+   Example 1.3. Set callid_prefix parameter
+...
+# using '%' as delimiter
+modparam("cfgt", "callid_prefix", "TEST-ID%")
+...

+ 45 - 0
modules/cfgt/cfgt.c

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+#include "cfgt_mod.h"
+#include "cfgt.h"
+#include "cfgt_int.h"
+
+/*!
+ * \brief cfgt module API export bind function
+ * \param api cfgt API
+ * \return 0 on success, -1 on failure
+ */
+int bind_cfgt(cfgt_api_t* api)
+{
+	if (!api) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+	if (init_flag==0) {
+		LM_ERR("configuration error - trying to bind to cfgt module"
+				" before being initialized\n");
+		return -1;
+	}
+
+	api->cfgt_process_route   = cfgt_process_route;
+	return 0;
+}

+ 40 - 0
modules/cfgt/cfgt.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifndef _CFGT_BIND_H
+#define _CFGT_BIND_H
+
+#include "../../sr_module.h"
+
+/* export not usable from scripts */
+#define NO_SCRIPT	-1
+
+typedef int (*cfgt_process_route_f)(struct sip_msg *msg, struct action *a);
+
+typedef struct cfgt_api {
+	cfgt_process_route_f cfgt_process_route;
+} cfgt_api_t;
+
+/*! cfgt API export bind function */
+typedef int (*bind_cfgt_t)(cfgt_api_t* api);
+
+#endif

+ 797 - 0
modules/cfgt/cfgt_int.c

@@ -0,0 +1,797 @@
+/**
+ *
+ * Copyright (C) 2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "../../events.h"
+#include "../../lib/kcore/cmpapi.h"
+#include "../../pvar.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+#include "cfgt_int.h"
+#include "cfgt_json.h"
+
+static str _cfgt_route_prefix[] = {
+	str_init("start|"),
+	str_init("exit|"),
+	str_init("drop|"),
+	str_init("return|"),
+	{0, 0}
+};
+cfgt_node_p _cfgt_node = NULL;
+cfgt_hash_p _cfgt_uuid = NULL;
+str cfgt_hdr_prefix = {"NGCP%", 5};
+str cfgt_basedir = {"/tmp", 4};
+int cfgt_mask = CFGT_DP_ALL;
+
+static int shm_str_hash_alloc(struct str_hash_table *ht, int size)
+{
+	ht->table = shm_malloc(sizeof(struct str_hash_head) * size);
+
+	if (!ht->table)
+		return -1;
+
+	ht->size = size;
+	return 0;
+}
+
+static int _cfgt_init_hashtable(struct str_hash_table *ht)
+{
+	if (shm_str_hash_alloc(ht, CFGT_HASH_SIZE) != 0)
+	{
+		LM_ERR("Error allocating shared memory hashtable\n");
+		return -1;
+	}
+
+	str_hash_init(ht);
+
+	return 0;
+}
+
+int _cfgt_pv_parse(str *param, pv_elem_p *elem)
+{
+	if (param->s && param->len > 0)
+	{
+		if (pv_parse_format(param, elem)<0)
+		{
+			LM_ERR("malformed or non AVP %.*s AVP definition\n",
+					param->len, param->s);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+void _cfgt_remove_uuid(const str *uuid)
+{
+	struct str_hash_head *head;
+	struct str_hash_entry *entry, *back;
+	int i;
+
+	if(_cfgt_uuid==NULL) return;
+	if(uuid)
+	{
+		lock_get(&_cfgt_uuid->lock);
+		entry = str_hash_get(&_cfgt_uuid->hash, uuid->s, uuid->len);
+		if(entry)
+		{
+			str_hash_del(entry);
+			shm_free(entry->key.s);
+			shm_free(entry);
+			LM_DBG("uuid[%.*s] removed from hash\n", uuid->len, uuid->s);
+		}
+		else LM_DBG("uuid[%.*s] not found in hash\n", uuid->len, uuid->s);
+		lock_release(&_cfgt_uuid->lock);
+	}
+	else
+	{
+		lock_get(&_cfgt_uuid->lock);
+		for(i=0; i<CFGT_HASH_SIZE; i++)
+		{
+			head = _cfgt_uuid->hash.table+i;
+			clist_foreach_safe(head, entry, back, next)
+			{
+				LM_DBG("uuid[%.*s] removed from hash\n",
+					entry->key.len, entry->key.s);
+				str_hash_del(entry);
+				shm_free(entry->key.s);
+				shm_free(entry);
+			}
+			lock_release(&_cfgt_uuid->lock);
+		}
+		LM_DBG("remove all uuids. done\n");
+	}
+}
+
+int _cfgt_get_uuid_id(cfgt_node_p node)
+{
+	struct str_hash_entry *entry;
+
+	if(_cfgt_uuid==NULL || node==NULL || node->uuid.len == 0) return -1;
+	lock_get(&_cfgt_uuid->lock);
+	entry = str_hash_get(&_cfgt_uuid->hash, node->uuid.s, node->uuid.len);
+	if(entry)
+	{
+		entry->u.n = entry->u.n + 1;
+		node->msgid = entry->u.n;
+	}
+	else
+	{
+		entry = shm_malloc(sizeof(struct str_hash_entry));
+		if(entry==NULL)
+		{
+			lock_release(&_cfgt_uuid->lock);
+			LM_ERR("No shared memory left\n");
+			return -1;
+		}
+		if (shm_str_dup(&entry->key, &node->uuid) != 0)
+		{
+			lock_release(&_cfgt_uuid->lock);
+			shm_free(entry);
+			LM_ERR("No shared memory left\n");
+			return -1;
+		}
+		entry->u.n = 1;
+		node->msgid = 1;
+		LM_DBG("Add new entry[%.*s]\n", node->uuid.len, node->uuid.s);
+		str_hash_add(&_cfgt_uuid->hash, entry);
+	}
+	lock_release(&_cfgt_uuid->lock);
+	LM_DBG("msgid:[%d]\n", node->msgid);
+	return 1;
+}
+
+int _cfgt_get_hdr_helper(struct sip_msg *msg, str *res, int mode)
+{
+	struct hdr_field *hf;
+	char *delimiter, *end;
+	str tmp = STR_NULL;
+
+	if(msg==NULL || (mode==0 && res==NULL))
+		return -1;
+
+	/* we need to be sure we have parsed all headers */
+	if(parse_headers(msg, HDR_EOH_F, 0)<0)
+	{
+		LM_ERR("error parsing headers\n");
+		return -1;
+	}
+
+	hf = msg->callid;
+	if(!hf) return 1;
+
+	if(strncmp(hf->body.s, cfgt_hdr_prefix.s, cfgt_hdr_prefix.len)==0)
+	{
+		tmp.s = hf->body.s+cfgt_hdr_prefix.len;
+		delimiter = tmp.s-1;
+		LM_DBG("Prefix detected. delimiter[%c]\n", *delimiter);
+		if(mode==0)
+		{
+			end = strchr(tmp.s, *delimiter);
+			if(end)
+			{
+				tmp.len = end-tmp.s;
+				if(pkg_str_dup(res, &tmp)<0)
+				{
+					LM_ERR("error copying header\n");
+					return -1;
+				}
+				LM_DBG("cfgtest uuid:[%.*s]\n", res->len, res->s);
+				return 0;
+			}
+		}
+		else
+		{
+			tmp.len = res->len;
+			LM_DBG("tmp[%.*s] res[%.*s]\n", tmp.len, tmp.s, res->len, res->s);
+			return STR_EQ(tmp, *res);
+		}
+	}
+	return 1; /* not found */
+}
+
+int _cfgt_get_hdr(struct sip_msg *msg, str *res)
+{
+	return _cfgt_get_hdr_helper(msg, res, 0);
+}
+
+int _cfgt_cmp_hdr(struct sip_msg *msg, str *res)
+{
+	return _cfgt_get_hdr_helper(msg, res, 1);
+}
+
+cfgt_node_p cfgt_create_node(struct sip_msg *msg)
+{
+	cfgt_node_p node;
+
+	node = (cfgt_node_p) pkg_malloc(sizeof(cfgt_node_t));
+	if(node==NULL)
+	{
+		LM_ERR("cannot allocate cfgtest msgnode\n");
+		return node;
+	}
+	memset(node, 0, sizeof(cfgt_node_t));
+	srjson_InitDoc(&node->jdoc, NULL);
+	if (msg)
+	{
+		node->msgid = msg->id;
+		LM_DBG("msgid:%d\n", node->msgid);
+		if(_cfgt_get_hdr(msg, &node->uuid)!=0 || &node->uuid.len==0)
+		{
+			LM_ERR("cannot get value of cfgtest uuid header!!\n");
+			goto error;
+		}
+	}
+	node->jdoc.root = srjson_CreateObject(&node->jdoc);
+	if(node->jdoc.root==NULL)
+	{
+		LM_ERR("cannot create json root\n");
+		goto error;
+	}
+	node->flow = srjson_CreateArray(&node->jdoc);
+	if(node->flow==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "flow\0", node->flow);
+	node->in = srjson_CreateArray(&node->jdoc);
+	if(node->in==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_in\0", node->in);
+	node->out = srjson_CreateArray(&node->jdoc);
+	if(node->out==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_out\0", node->out);
+	LM_DBG("node created\n");
+	return node;
+
+error:
+	srjson_DestroyDoc(&node->jdoc);
+	pkg_free(node);
+	return NULL;
+}
+
+void _cfgt_remove_node(cfgt_node_p node)
+{
+	if(!node) return;
+	srjson_DestroyDoc(&node->jdoc);
+	if(node->uuid.s) pkg_free(node->uuid.s);
+	while(node->flow_head)
+	{
+		node->route = node->flow_head;
+		node->flow_head = node->route->next;
+		pkg_free(node->route);
+		node->route = NULL;
+	}
+	pkg_free(node);
+}
+
+int _cfgt_get_filename(int msgid, str uuid, str *dest, int *dir)
+{
+	int i, lid;
+	char buff_id[INT2STR_MAX_LEN];
+	char *sid;
+	if(dest==NULL || uuid.len == 0) return -1;
+
+	dest->len = cfgt_basedir.len + uuid.len;
+	if(cfgt_basedir.s[cfgt_basedir.len-1]!='/')
+		dest->len = dest->len + 1;
+	sid = sint2strbuf(msgid, buff_id, INT2STR_MAX_LEN, &lid);
+	dest->len += lid + 6;
+	dest->s = (char *) pkg_malloc((dest->len*sizeof(char)+1));
+	if(dest->s==NULL)
+	{
+		LM_ERR("no more memory.\n");
+		return -1;
+	}
+	strncpy(dest->s, cfgt_basedir.s, cfgt_basedir.len);
+	i = cfgt_basedir.len;
+	if(cfgt_basedir.s[cfgt_basedir.len-1]!='/')
+	{
+		strncpy(dest->s+i, "/", 1);
+		i = i + 1;
+	}
+	strncpy(dest->s+i, uuid.s, uuid.len);
+	i = i + uuid.len; (*dir) = i;
+	strncpy(dest->s+i, "\0", 1);
+	i = i + 1;
+	strncpy(dest->s+i, sid, lid);
+	i = i + lid;
+	strncpy(dest->s+i, ".json\0", 6);
+	return 0;
+}
+
+int _cfgt_node2json(cfgt_node_p node)
+{
+       srjson_t *jobj;
+
+       if(!node) return -1;
+       jobj = srjson_CreateStr(&node->jdoc, node->uuid.s, node->uuid.len);
+       if(jobj==NULL)
+       {
+               LM_ERR("cannot create json object\n");
+               return -1;
+       }
+       srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "uuid\0", jobj);
+
+       jobj = srjson_CreateNumber(&node->jdoc, (double)node->msgid);
+       if(jobj==NULL)
+       {
+               LM_ERR("cannot create json object\n");
+               return -1;
+       }
+       srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "msgid\0", jobj);
+       return 0;
+}
+
+void cfgt_save_node(cfgt_node_p node)
+{
+	FILE *fp;
+	str dest = STR_NULL;
+	int dir = 0;
+	if(_cfgt_get_filename(node->msgid, node->uuid, &dest, &dir)<0)
+	{
+		LM_ERR("can't build filename\n");
+		return;
+	}
+	LM_DBG("dir [%s]\n", dest.s);
+	mkdir(dest.s, S_IRWXO|S_IXGRP|S_IRWXU);
+	dest.s[dir] = '/';
+	fp = fopen(dest.s, "w");
+	LM_DBG("file [%s]\n", dest.s);
+	if(fp) {
+		pkg_free(dest.s);
+		dest.s = srjson_Print(&node->jdoc, node->jdoc.root);
+	    if(dest.s==NULL)
+        {
+           LM_ERR("Cannot get the json string\n");
+           fclose(fp);
+           return;
+        }
+        if(fputs(dest.s, fp)<0){
+        	LM_ERR("failed writing to file\n");
+        }
+        fclose(fp);
+        node->jdoc.free_fn(dest.s);
+	}
+	else {
+		LM_ERR("Can't open file [%s] to write\n", dest.s);
+		pkg_free(dest.s);
+	}
+}
+
+void _cfgt_print_node(cfgt_node_p node, int json)
+{
+	char *buf = NULL;
+	cfgt_str_list_p route;
+
+	if(!node) return;
+	if(node->flow_head)
+	{
+		route = node->flow_head;
+		while(route)
+		{
+			if(route == node->route)
+				LM_DBG("[--[%.*s][%d]--]\n",
+					route->s.len, route->s.s, route->type);
+			else LM_DBG("[%.*s][%d]\n",
+					route->s.len, route->s.s, route->type);
+			route = route->next;
+		}
+	}
+	else LM_DBG("flow:empty\n");
+	if(json) {
+		buf = srjson_PrintUnformatted(&node->jdoc, node->jdoc.root);
+		if(buf==NULL)
+		{
+			LM_ERR("Cannot get the json string\n");
+			return;
+		}
+		LM_DBG("node[%p]: id:[%d] uuid:[%.*s] info:[%s]\n",
+			node, node->msgid, node->uuid.len, node->uuid.s, buf);
+		node->jdoc.free_fn(buf);
+	}
+}
+
+int _cfgt_set_dump(struct sip_msg *msg, cfgt_node_p node, str *flow)
+{
+	srjson_t *f, *vars;
+
+	if(node==NULL || flow == NULL) return -1;
+	vars = srjson_CreateObject(&node->jdoc);
+	if(vars==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	if(cfgt_get_json(msg, 30, &node->jdoc, vars)<0)
+	{
+		LM_ERR("cannot get var info\n");
+		return -1;
+	}
+	f = srjson_CreateObject(&node->jdoc);
+	if(f==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		srjson_Delete(&node->jdoc, vars);
+		return -1;
+	}
+	srjson_AddStrItemToObject(&node->jdoc, f,
+		flow->s, flow->len, vars);
+	srjson_AddItemToArray(&node->jdoc, node->flow, f);
+	LM_DBG("node[%.*s] flow created\n", flow->len, flow->s);
+	return 0;
+}
+
+void _cfgt_set_type(cfgt_str_list_p route, struct action *a)
+{
+	switch(a->type)
+	{
+		case DROP_T:
+			if(a->val[1].u.number&DROP_R_F) {
+				route->type = CFGT_DROP_D;
+				LM_DBG("set[%.*s]->CFGT_DROP_D\n", route->s.len, route->s.s);
+			}
+			if(a->val[1].u.number&RETURN_R_F){
+				route->type = CFGT_DROP_R;
+				LM_DBG("set[%.*s]->CFGT_DROP_R\n", route->s.len, route->s.s);
+			}
+			else {
+				route->type = CFGT_DROP_E;
+				LM_DBG("set[%.*s]->CFGT_DROP_E\n", route->s.len, route->s.s);
+			}
+			break;
+		case ROUTE_T:
+			route->type = CFGT_ROUTE;
+			LM_DBG("set[%.*s]->CFGT_ROUTE\n", route->s.len, route->s.s);
+			break;
+		default:
+			if(route->type!=CFGT_DROP_E)
+			{
+				route->type = CFGT_DROP_R;
+				LM_DBG("[%.*s] no relevant action: CFGT_DROP_R[%d]\n",
+					route->s.len, route->s.s, a->type);
+			}
+			else
+			{
+				LM_DBG("[%.*s] already set to CFGT_DROP_E[%d]\n",
+					route->s.len, route->s.s, a->type);
+			}
+			break;
+	}
+}
+
+int _cfgt_add_routename(cfgt_node_p node, struct action *a,
+		str *routename)
+{
+	cfgt_str_list_p route;
+	int ret = 0;
+
+	if(!node->route) /* initial */
+	{
+		node->route = pkg_malloc(sizeof(cfgt_str_list_t));
+		if(!node->route)
+		{
+			LM_ERR("No more pkg mem\n");
+			return -1;
+		}
+		memset(node->route, 0, sizeof(cfgt_str_list_t));
+		node->flow_head = node->route;
+		node->route->type = CFGT_ROUTE;
+		ret = 1;
+	}
+	else
+	{
+		LM_DBG("actual routename:[%.*s][%d]\n", node->route->s.len,
+			node->route->s.s, node->route->type);
+		if(node->route->prev)
+			LM_DBG("prev routename:[%.*s][%d]\n", node->route->prev->s.len,
+				node->route->prev->s.s,	node->route->prev->type);
+		if(node->route->next)
+			LM_DBG("next routename:[%.*s][%d]\n", node->route->next->s.len,
+				node->route->next->s.s,	node->route->next->type);
+		if(STR_EQ(*routename, node->route->s))
+		{
+			LM_DBG("same route\n");
+			_cfgt_set_type(node->route, a);
+			return 2;
+		}
+		else if(node->route->prev &&
+				STR_EQ(*routename, node->route->prev->s))
+		{
+			LM_DBG("back to route[%.*s]\n", node->route->prev->s.len,
+				node->route->prev->s.s);
+			_cfgt_set_type(node->route->prev, a);
+			return 3;
+		}
+		route = pkg_malloc(sizeof(cfgt_str_list_t));
+		if(!route)
+		{
+			LM_ERR("No more pkg mem\n");
+			return -1;
+		}
+		memset(route, 0, sizeof(cfgt_str_list_t));
+		route->prev = node->route;
+		node->route->next = route;
+		node->route = route;
+		_cfgt_set_type(node->route, a);
+	}
+	node->route->s.s = routename->s;
+	node->route->s.len = routename->len;
+	LM_DBG("add[%d] route:[%.*s]\n", ret, node->route->s.len, node->route->s.s);
+	_cfgt_print_node(node, 0);
+	return ret;
+}
+
+void _cfgt_del_routename(cfgt_node_p node)
+{
+	if(node->route->next!=NULL) {
+		LM_ERR("wtf!! route->next[%p] not null!!\n", node->route->next);
+		_cfgt_print_node(node, 0);
+	}
+	LM_DBG("del route[%.*s]\n", node->route->s.len, node->route->s.s);
+	node->route = node->route->prev;
+	pkg_free(node->route->next);
+	node->route->next = NULL;
+}
+/* dest has to be freed */
+int _cfgt_node_get_flowname(cfgt_str_list_p route, int *indx, str *dest)
+{
+	int i;
+	if(route==NULL) return -1;
+	LM_DBG("routename:[%.*s][%d]\n", route->s.len, route->s.s,
+		route->type);
+	if(indx) i = *indx;
+	else i = route->type-1;
+	if(str_append(&_cfgt_route_prefix[i],
+		&route->s, dest)<0)
+	{
+		LM_ERR("Cannot create route name\n");
+		return -1;
+	}
+	return 0;
+}
+
+int cfgt_process_route(struct sip_msg *msg, struct action *a)
+{
+	str routename;
+	int ret = -1;
+	int indx = 0;
+	str flowname = STR_NULL;
+	if(!_cfgt_node) {
+		LM_ERR("node empty\n");
+		return -1;
+	}
+	if (a->rname==NULL) {
+		LM_DBG("no routename. type:%d\n", a->type);
+		return 0;
+	}
+	LM_DBG("route from action:[%s]\n", a->rname);
+	routename.s = a->rname; routename.len = strlen(a->rname);
+	switch(_cfgt_add_routename(_cfgt_node, a, &routename))
+	{
+		case 2: /* same name */
+			return 0;
+		case 1: /* initial */
+			LM_DBG("Initial route[%.*s]. dump vars\n",
+				_cfgt_node->route->s.len, _cfgt_node->route->s.s);
+			if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			break;
+		case 0: /* new */
+			LM_DBG("Change from[%.*s] route to route[%.*s]. dump vars\n",
+				_cfgt_node->route->prev->s.len, _cfgt_node->route->prev->s.s,
+				_cfgt_node->route->s.len, _cfgt_node->route->s.s);
+			if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			break;
+		case 3: /* back to previous */
+			if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			_cfgt_del_routename(_cfgt_node);
+			break;
+		default:
+			return -1;
+	}
+	if(flowname.s) pkg_free(flowname.s);
+	return ret;
+}
+
+/*
+TODO:
+- parse first line, check if is SIP
+- parse for header cfgtest
+*/
+int cfgt_msgin(void *data)
+{
+	srjson_t *jobj;
+	str *buf = (str *) data;
+	if(buf==NULL) return 0;
+	if(_cfgt_node) {
+		cfgt_save_node(_cfgt_node);
+		_cfgt_remove_node(_cfgt_node);
+		LM_DBG("node removed\n");
+		_cfgt_node = NULL;
+	}
+	LM_DBG("msg in:{%.*s}\n", buf->len, buf->s);
+	_cfgt_node = cfgt_create_node(NULL);
+	if(_cfgt_node)
+	{
+		jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len);
+		if(jobj==NULL)
+		{
+			LM_ERR("cannot create json object\n");
+			return -1;
+		}
+		srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->in, jobj);
+		return 0;
+	}
+	LM_ERR("_cfgt_node empty\n");
+	return -1;
+}
+
+int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar) {
+	str unknown = {"unknown", 7};
+
+	if(_cfgt_node)
+	{
+		if (_cfgt_node->msgid == 0)
+		{
+			LM_DBG("new node\n");
+			if(_cfgt_get_hdr(msg, &_cfgt_node->uuid)!=0 ||
+				_cfgt_node->uuid.len==0)
+			{
+				LM_ERR("cannot get value of cfgtest uuid header."
+					" Using unknown\n");
+				pkg_str_dup(&_cfgt_node->uuid, &unknown);
+			}
+			return _cfgt_get_uuid_id(_cfgt_node);
+		}
+		else
+		{
+			LM_DBG("_cfgt_node->uuid:[%.*s]\n", _cfgt_node->uuid.len,
+				_cfgt_node->uuid.s);
+			if(_cfgt_cmp_hdr(msg, &_cfgt_node->uuid))
+			{
+				LM_DBG("same uuid\n");
+				return 1;
+			}
+			else {
+				LM_DBG("different uuid\n");
+			}
+		}
+	}
+	else { LM_ERR("node empty??\n"); }
+	_cfgt_node = cfgt_create_node(msg);
+	if(_cfgt_node) {
+		LM_DBG("node created\n");
+		return 1;
+	}
+	return -1;
+}
+int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar) {
+	str flowname = STR_NULL;
+
+	if(_cfgt_node) {
+		LM_DBG("dump last flow\n");
+		if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0)
+			LM_ERR("cannot create flowname\n");
+		else _cfgt_set_dump(msg, _cfgt_node, &flowname);
+		if(flowname.s) pkg_free(flowname.s);
+		cfgt_save_node(_cfgt_node);
+	}
+	return 1;
+}
+
+int cfgt_msgout(void *data)
+{
+	srjson_t *jobj;
+	str *buf = (str *) data;
+	if(buf==NULL) return 0;
+	LM_DBG("msg out:{%.*s}\n", buf->len, buf->s);
+
+	if(_cfgt_node)
+	{
+		jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len);
+		if(jobj==NULL)
+		{
+			LM_ERR("cannot create json object\n");
+			return -1;
+		}
+		srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->out, jobj);
+		return 0;
+	}
+	LM_ERR("node empty\n");
+	return -1;
+}
+
+/**
+ *
+ */
+static const char* cfgt_rpc_mask_doc[2] = {
+	"Specify module mask",
+	0
+};
+
+static void cfgt_rpc_mask(rpc_t* rpc, void* ctx){
+	int mask = CFGT_DP_ALL;
+
+	if (rpc->scan(ctx, "*d", &mask) != 1)
+	{
+		rpc->fault(ctx, 500, "invalid parameters");
+		return;
+	}
+	cfgt_mask = mask;
+	rpc->add(ctx, "s", "200 ok");
+}
+
+rpc_export_t cfgt_rpc[] = {
+	{"dbg.mask", cfgt_rpc_mask, cfgt_rpc_mask_doc, 0},
+	{0, 0, 0, 0}
+};
+
+int cfgt_init(void)
+{
+	if (rpc_register_array(cfgt_rpc)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	_cfgt_uuid = shm_malloc(sizeof(cfgt_hash_t));
+	if(_cfgt_uuid==NULL)
+	{
+		LM_ERR("Cannot allocate shared memory\n");
+		return -1;
+	}
+	if(!lock_init(&_cfgt_uuid->lock))
+	{
+		LM_ERR("cannot init the lock\n");
+		shm_free(_cfgt_uuid);
+		_cfgt_uuid = NULL;
+		return -1;
+	}
+	if(_cfgt_init_hashtable(&_cfgt_uuid->hash)<0)
+		return -1;
+	sr_event_register_cb(SREV_NET_DATA_IN, cfgt_msgin);
+	sr_event_register_cb(SREV_NET_DATA_OUT, cfgt_msgout);
+	return 0;
+}

+ 67 - 0
modules/cfgt/cfgt_int.h

@@ -0,0 +1,67 @@
+/**
+ *
+ * Copyright (C) 2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef _CFGT_INT_H_
+#define _CFGT_INT_H_
+
+#include "../../lib/srutils/srjson.h"
+#include "../../locking.h"
+#include "../../route_struct.h"
+#include "../../str_hash.h"
+
+#define CFGT_HASH_SIZE 32
+
+enum _cfgt_action_type {
+	CFGT_ROUTE=1,
+	CFGT_DROP_E, CFGT_DROP_D, CFGT_DROP_R
+};
+
+typedef struct _cfgt_hash
+{
+	gen_lock_t lock;
+	struct str_hash_table hash;
+	str save_uuid; /* uuid to be save */
+} cfgt_hash_t, *cfgt_hash_p;
+
+typedef struct _cfgt_str_list
+{
+	str s;
+	enum _cfgt_action_type type;
+	struct _cfgt_str_list *next, *prev;
+} cfgt_str_list_t, *cfgt_str_list_p;
+
+typedef struct _cfgt_node
+{
+	srjson_doc_t jdoc;
+	str uuid;
+	int msgid;
+	cfgt_str_list_p flow_head;
+	cfgt_str_list_p route;
+	srjson_t *in, *out, *flow;
+	struct _cfgt_node *next, *prev;
+} cfgt_node_t, *cfgt_node_p;
+
+int cfgt_init(void);
+cfgt_node_p cfgt_create_node(struct sip_msg *msg);
+int cfgt_process_route(struct sip_msg *msg, struct action *a);
+int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar);
+int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar);
+#endif

+ 389 - 0
modules/cfgt/cfgt_json.c

@@ -0,0 +1,389 @@
+/**
+ *
+ * Copyright (C) 2013-2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+
+#include "../../pvar.h"
+#include "../../mem/shm_mem.h"
+#include "../../xavp.h"
+#include "../pv/pv_xavp.h"
+
+#include "cfgt_json.h"
+
+int _cfgt_get_array_avp_vals(struct sip_msg *msg,
+		pv_param_t *param, srjson_doc_t *jdoc, srjson_t **jobj,
+		str *item_name)
+{
+	struct usr_avp *avp;
+	unsigned short name_type;
+	int_str avp_name;
+	int_str avp_value;
+	struct search_state state;
+	srjson_t *jobjt;
+	memset(&state, 0, sizeof(struct search_state));
+
+	if(pv_get_avp_name(msg, param, &avp_name, &name_type)!=0)
+	{
+		LM_ERR("invalid name\n");
+		return -1;
+	}
+	*jobj = srjson_CreateArray(jdoc);
+	if(*jobj==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	if ((avp=search_first_avp(name_type, avp_name, &avp_value, &state))==0)
+	{
+		goto ok;
+	}
+	do
+	{
+		if(avp->flags & AVP_VAL_STR)
+		{
+			jobjt = srjson_CreateStr(jdoc, avp_value.s.s, avp_value.s.len);
+			if(jobjt==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+		} else {
+			jobjt = srjson_CreateNumber(jdoc, avp_value.n);
+			if(jobjt==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+		}
+		srjson_AddItemToArray(jdoc, *jobj, jobjt);
+	} while ((avp=search_next_avp(&state, &avp_value))!=0);
+ok:
+	item_name->s = avp_name.s.s;
+	item_name->len = avp_name.s.len;
+	return 0;
+}
+#define CFGT_XAVP_DUMP_SIZE 32
+static str* _cfgt_xavp_dump[CFGT_XAVP_DUMP_SIZE];
+int _cfgt_xavp_dump_lookup(pv_param_t *param)
+{
+	unsigned int i = 0;
+	pv_xavp_name_t *xname;
+
+	if(param==NULL)
+		return -1;
+
+	xname = (pv_xavp_name_t*)param->pvn.u.dname;
+
+	while(_cfgt_xavp_dump[i]!=NULL&&i<CFGT_XAVP_DUMP_SIZE)
+	{
+		if(_cfgt_xavp_dump[i]->len==xname->name.len)
+		{
+			if(strncmp(_cfgt_xavp_dump[i]->s, xname->name.s, xname->name.len)==0)
+				return 1; /* already dump before */
+		}
+		i++;
+	}
+	if(i==CFGT_XAVP_DUMP_SIZE)
+	{
+		LM_WARN("full _cfgt_xavp_dump cache array\n");
+		return 0; /* end cache names */
+	}
+	_cfgt_xavp_dump[i] = &xname->name;
+	return 0;
+}
+
+void _cfgt_get_obj_xavp_val(sr_xavp_t *avp, srjson_doc_t *jdoc, srjson_t **jobj)
+{
+	static char _pv_xavp_buf[128];
+	int result = 0;
+
+	switch(avp->val.type) {
+		case SR_XTYPE_NULL:
+			*jobj = srjson_CreateNull(jdoc);
+		break;
+		case SR_XTYPE_INT:
+			*jobj = srjson_CreateNumber(jdoc, avp->val.v.i);
+		break;
+		case SR_XTYPE_STR:
+			*jobj = srjson_CreateStr(jdoc, avp->val.v.s.s, avp->val.v.s.len);
+		break;
+		case SR_XTYPE_TIME:
+			result = snprintf(_pv_xavp_buf, 128, "%lu", (long unsigned)avp->val.v.t);
+		break;
+		case SR_XTYPE_LONG:
+			result = snprintf(_pv_xavp_buf, 128, "%ld", (long unsigned)avp->val.v.l);
+		break;
+		case SR_XTYPE_LLONG:
+			result = snprintf(_pv_xavp_buf, 128, "%lld", avp->val.v.ll);
+		break;
+		case SR_XTYPE_XAVP:
+			result = snprintf(_pv_xavp_buf, 128, "<<xavp:%p>>", avp->val.v.xavp);
+		break;
+		case SR_XTYPE_DATA:
+			result = snprintf(_pv_xavp_buf, 128, "<<data:%p>>", avp->val.v.data);
+		break;
+		default:
+			LM_WARN("unknown data type\n");
+			*jobj = srjson_CreateNull(jdoc);
+	}
+	if(result<0)
+	{
+		LM_ERR("cannot convert to str\n");
+		*jobj = srjson_CreateNull(jdoc);
+	}
+	else if(*jobj==NULL)
+	{
+		*jobj = srjson_CreateStr(jdoc, _pv_xavp_buf, 128);
+	}
+}
+
+int _cfgt_get_obj_avp_vals(str name, sr_xavp_t *xavp, srjson_doc_t *jdoc, srjson_t **jobj)
+{
+	sr_xavp_t *avp = NULL;
+	srjson_t *jobjt = NULL;
+
+	*jobj = srjson_CreateArray(jdoc);
+	if(*jobj==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	avp = xavp;
+	while(avp!=NULL&&!STR_EQ(avp->name,name))
+	{
+		avp = avp->next;
+	}
+	while(avp!=NULL)
+	{
+		_cfgt_get_obj_xavp_val(avp, jdoc, &jobjt);
+		srjson_AddItemToArray(jdoc, *jobj, jobjt);
+		jobjt = NULL;
+		avp = xavp_get_next(avp);
+	}
+
+	return 0;
+}
+
+int _cfgt_get_obj_xavp_vals(struct sip_msg *msg,
+		pv_param_t *param, srjson_doc_t *jdoc, srjson_t **jobjr,
+		str *item_name)
+{
+	pv_xavp_name_t *xname = (pv_xavp_name_t*)param->pvn.u.dname;
+	sr_xavp_t *xavp = NULL;
+	sr_xavp_t *avp = NULL;
+	srjson_t *jobj = NULL;
+	srjson_t *jobjt = NULL;
+	struct str_list *keys;
+	struct str_list *k;
+
+	*jobjr = srjson_CreateArray(jdoc);
+	if(*jobjr==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+
+	item_name->s = xname->name.s;
+	item_name->len = xname->name.len;
+	xavp = xavp_get_by_index(&xname->name, 0, NULL);
+	if(xavp==NULL)
+	{
+		return 0; /* empty */
+	}
+
+	do
+	{
+		if(xavp->val.type==SR_XTYPE_XAVP)
+		{
+			avp = xavp->val.v.xavp;
+			jobj = srjson_CreateObject(jdoc);
+			if(jobj==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+			keys = xavp_get_list_key_names(xavp);
+			if(keys!=NULL)
+			{
+				do
+				{
+					_cfgt_get_obj_avp_vals(keys->s, avp, jdoc, &jobjt);
+					srjson_AddStrItemToObject(jdoc, jobj, keys->s.s,
+						keys->s.len, jobjt);
+					k = keys;
+					keys = keys->next;
+					pkg_free(k);
+					jobjt = NULL;
+				}while(keys!=NULL);
+			}
+		}
+		if(jobj!=NULL)
+		{
+			srjson_AddItemToArray(jdoc, *jobjr, jobj);
+			jobj = NULL;
+		}
+	}while((xavp = xavp_get_next(xavp))!=0);
+
+	return 0;
+}
+
+int cfgt_get_json(struct sip_msg* msg, unsigned int mask, srjson_doc_t *jdoc,
+	srjson_t *head)
+{
+	int i;
+	pv_value_t value;
+	pv_cache_t **_pv_cache = pv_cache_get_table();
+	pv_cache_t *el = NULL;
+	srjson_t *jobj = NULL;
+	str item_name = STR_NULL;
+	static char iname[128];
+
+	if(_pv_cache==NULL)
+	{
+		LM_ERR("cannot access pv_cache\n");
+		return -1;
+	}
+	if(jdoc==NULL){
+		LM_ERR("jdoc is null\n");
+		return -1;
+	}
+	if(head==NULL){
+		LM_ERR("head is null\n");
+		return -1;
+	}
+
+	memset(_cfgt_xavp_dump, 0, sizeof(str*)*CFGT_XAVP_DUMP_SIZE);
+	for(i=0;i<PV_CACHE_SIZE;i++)
+	{
+		el = _pv_cache[i];
+		while(el)
+		{
+			if(!(el->spec.type==PVT_AVP||
+				el->spec.type==PVT_SCRIPTVAR||
+				el->spec.type==PVT_XAVP||
+				el->spec.type==PVT_OTHER)||
+				!((el->spec.type==PVT_AVP&&mask&CFGT_DP_AVP)||
+				(el->spec.type==PVT_XAVP&&mask&CFGT_DP_XAVP)||
+				(el->spec.type==PVT_SCRIPTVAR&&mask&CFGT_DP_SCRIPTVAR)||
+				(el->spec.type==PVT_OTHER&&mask&CFGT_DP_OTHER))||
+				(el->spec.trans!=NULL))
+			{
+				el = el->next;
+				continue;
+			}
+			jobj = NULL;
+			item_name.len = 0;
+			item_name.s = 0;
+			iname[0] = '\0';
+			if(el->spec.type==PVT_AVP)
+			{
+				if(el->spec.pvp.pvi.type==PV_IDX_ALL||
+					(el->spec.pvp.pvi.type==PV_IDX_INT&&el->spec.pvp.pvi.u.ival!=0))
+				{
+					el = el->next;
+					continue;
+				}
+				else
+				{
+					if(_cfgt_get_array_avp_vals(msg, &el->spec.pvp, jdoc, &jobj, &item_name)!=0)
+					{
+						LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+						el = el->next;
+						continue;
+					}
+					if(srjson_GetArraySize(jdoc, jobj)==0 && !(mask&CFGT_DP_NULL))
+					{
+						el = el->next;
+						continue;
+					}
+					snprintf(iname, 128, "$avp(%.*s)", item_name.len, item_name.s);
+				}
+			}
+			else if(el->spec.type==PVT_XAVP)
+			{
+				if(_cfgt_xavp_dump_lookup(&el->spec.pvp)!=0)
+				{
+					el = el->next;
+					continue;
+				}
+				if(_cfgt_get_obj_xavp_vals(msg, &el->spec.pvp, jdoc, &jobj, &item_name)!=0)
+				{
+					LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+					el = el->next;
+					continue;
+				}
+				if(srjson_GetArraySize(jdoc, jobj)==0 && !(mask&CFGT_DP_NULL))
+				{
+					el = el->next;
+					continue;
+				}
+				snprintf(iname, 128, "$xavp(%.*s)", item_name.len, item_name.s);
+			}
+			else
+			{
+				if(pv_get_spec_value(msg, &el->spec, &value)!=0)
+				{
+					LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+					el = el->next;
+					continue;
+				}
+				if(value.flags&(PV_VAL_NULL|PV_VAL_EMPTY|PV_VAL_NONE))
+				{
+					if(mask&CFGT_DP_NULL)
+					{
+						jobj = srjson_CreateNull(jdoc);
+					}
+					else
+					{
+						el = el->next;
+						continue;
+					}
+				}else if(value.flags&(PV_VAL_INT)){
+					jobj = srjson_CreateNumber(jdoc, value.ri);
+				}else if(value.flags&(PV_VAL_STR)){
+					jobj = srjson_CreateStr(jdoc, value.rs.s, value.rs.len);
+				}else {
+					LM_WARN("el->pvname[%.*s] value[%d] unhandled\n", el->pvname.len, el->pvname.s,
+						value.flags);
+					el = el->next;
+					continue;
+				}
+				if(jobj==NULL)
+				{
+					LM_ERR("el->pvname[%.*s] empty json object\n", el->pvname.len,
+						el->pvname.s);
+					goto error;
+				}
+				snprintf(iname, 128, "%.*s", el->pvname.len, el->pvname.s);
+			}
+			if(jobj!=NULL)
+			{
+				srjson_AddItemToObject(jdoc, head, iname, jobj);
+			}
+			el = el->next;
+		}
+	}
+	return 0;
+
+error:
+	srjson_Delete(jdoc, head);
+	return -1;
+}

+ 38 - 0
modules/cfgt/cfgt_json.h

@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2013-2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _CFGT_JSON_H
+#define _CFGT_JSON_H
+
+#include "../../lib/srutils/srjson.h"
+#include "../../route_struct.h"
+
+#define CFGT_DP_NULL       1
+#define CFGT_DP_AVP        2
+#define CFGT_DP_SCRIPTVAR  4
+#define CFGT_DP_XAVP       8
+#define CFGT_DP_OTHER     16
+#define CFGT_DP_ALL       31
+
+int cfgt_get_json(struct sip_msg* msg, unsigned int mask, srjson_doc_t *jdoc,
+	srjson_t *head);
+#endif

+ 112 - 0
modules/cfgt/cfgt_mod.c

@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#include "../../events.h"
+#include "../../script_cb.h"
+#include "../../sr_module.h"
+
+#include "cfgt_mod.h"
+#include "cfgt_int.h"
+#include "cfgt_json.h"
+#include "cfgt.h"
+
+MODULE_VERSION
+
+static int mod_init(void);        /*!< Module initialization function */
+static void destroy(void);        /*!< Module destroy function */
+static int child_init(int rank);  /*!< Per-child init function */
+
+extern int bind_cfgt(cfgt_api_t* api);
+
+/*! flag to protect against wrong initialization */
+unsigned int init_flag = 0;
+extern int cfgt_mask;
+extern str cfgt_basedir;
+extern str cfgt_hdr_prefix;
+
+/*! \brief
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"cfgt_bind_cfgt", (cmd_function)bind_cfgt, 1, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}
+};
+
+/*! \brief
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"basedir",  PARAM_STR, &cfgt_basedir},
+	{"mask", INT_PARAM, &cfgt_mask },
+	{"callid_prefix", PARAM_STR, &cfgt_hdr_prefix },
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"cfgt",
+	DEFAULT_DLFLAGS, /*!< dlopen flags */
+	cmds,       /*!< Exported functions */
+	params,     /*!< Export parameters */
+	0,          /*!< exported statistics */
+	0,          /*!< exported MI functions */
+	0,          /*!< exported pseudo-variables */
+	0,          /*!< extra processes */
+	mod_init,   /*!< Module initialization function */
+	0,          /*!< Response function */
+	destroy,    /*!< Destroy function */
+	child_init  /*!< Child initialization function */
+};
+
+/*! \brief
+ * Module initialization function
+ */
+static int mod_init(void)
+{
+	unsigned int ALL = REQUEST_CB+FAILURE_CB+ONREPLY_CB
+		+BRANCH_CB+ONSEND_CB+ERROR_CB+LOCAL_CB+EVENT_CB+BRANCH_FAILURE_CB;
+	if(cfgt_init()<0) return -1;
+	if (register_script_cb(cfgt_pre, PRE_SCRIPT_CB|ALL, 0) != 0)
+	{
+		LM_ERR("could not insert PRE_SCRIPT callback");
+		return -1;
+	}
+	if (register_script_cb(cfgt_post, POST_SCRIPT_CB|ALL, 0) != 0)
+	{
+		LM_ERR("could not insert POST_SCRIPT callback");
+		return -1;
+	}
+
+	init_flag = 1;
+	return 0;
+}
+
+static int child_init(int _rank)
+{
+	return 0;
+}
+
+/*! \brief
+ * Module destroy function
+ */
+static void destroy(void)
+{
+}

+ 29 - 0
modules/cfgt/cfgt_mod.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifndef _CFGT_MOD_H
+#define _CFGT_MOD_H
+
+/*! flag to protect against wrong initialization */
+extern unsigned int init_flag;
+
+#endif

+ 4 - 0
modules/cfgt/doc/Makefile

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

+ 36 - 0
modules/cfgt/doc/cfgt.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>cfgt Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Victor</firstname>
+		<surname>Seva</surname>
+		<affiliation><orgname>sipwise.com</orgname></affiliation>
+	    </author>
+	    <editor>
+		<firstname>Victor</firstname>
+		<surname>Seva</surname>
+		    <email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2015</year>
+	    <holder>Victor Seva (sipwise.com)</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+	<xi:include href="cfgt_admin.xml"/>
+
+</book>

+ 158 - 0
modules/cfgt/doc/cfgt_admin.xml

@@ -0,0 +1,158 @@
+<?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 provides a report of the way &kamailioname; configuration
+		has been executed as part of a unit test for different
+		SIP scenarios.
+	</para>
+	<para>
+		In order to identify defferent scenarios a prefix string should be
+		used at Call-ID header.
+	</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>None</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>Parameters</title>
+	<section id="cfg.p.basedir">
+		<title><varname>basedir</varname> (string)</title>
+		<para>
+			Control where the config reports should be stored. The dir must
+			exists and &kamailioname; must have perms to write on it.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>/tmp</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>cfgtrace</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="cfgt.p.mask">
+		<title><varname>mask</varname> (int)</title>
+		<itemizedlist>
+			<para><emphasis>mask</emphasis> - Control the type of vars it should
+			display in the report:
+			</para>
+			<itemizedlist>
+			<listitem><para>
+			  1 - dump null values
+			</para></listitem>
+			<listitem><para>
+			  2 - dump avp vars
+			</para></listitem>
+			<listitem><para>
+			  4 - dump script vars
+			</para></listitem>
+			<listitem><para>
+			  8 - dump xavp vars
+			</para></listitem>
+			<listitem><para>
+			  16 - dump DP_OTHER vars
+			</para></listitem>
+			<listitem><para>
+			  32 - dump ALL vars
+			</para></listitem>
+			</itemizedlist>
+		</itemizedlist>
+		<para>
+		<emphasis>
+			Default value is <quote>32</quote> (ALL).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>mask</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# dump xavp(8) and avp(4) vars
+modparam("cfgt", "mask", 12)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="cfgt.p.callid_prefix">
+		<title><varname>callid_prefix</varname> (string)</title>
+		<para>
+			Prefix used to indentify test scenario messages. Last char of the
+			string will be used as delimiter.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NGCP%</quote>
+			(using <quote>%</quote> as delimiter).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>callid_prefix</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# using '%' as delimiter
+modparam("cfgt", "callid_prefix", "TEST-ID%")
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+
+<!--
+	<section>
+		<title>Usage</title>
+		<para>
+			TODO: add some more info of how this is been used
+		</para>
+	</section>
+-->
+</chapter>

+ 31 - 14
modules/debugger/README

@@ -41,6 +41,7 @@ Daniel-Constantin Mierla
               3.14. log_assign (int)
               3.14. log_assign (int)
               3.15. cfgpkgcheck (int)
               3.15. cfgpkgcheck (int)
               3.16. reset_msgid (int)
               3.16. reset_msgid (int)
+              3.17. cfgtest (int)
 
 
         4. Functions
         4. Functions
 
 
@@ -76,9 +77,10 @@ Daniel-Constantin Mierla
    1.14. Set log_assign parameter
    1.14. Set log_assign parameter
    1.15. Set cfgpkgcheck parameter
    1.15. Set cfgpkgcheck parameter
    1.16. Set reset_msgid parameter
    1.16. Set reset_msgid parameter
-   1.17. dbg_breakpoint usage
-   1.18. dbg_pv_dump usage
-   1.19. dbg_sip_msg usage
+   1.17. Set cfgtest parameter
+   1.18. dbg_breakpoint usage
+   1.19. dbg_pv_dump usage
+   1.20. dbg_sip_msg usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -108,6 +110,7 @@ Chapter 1. Admin Guide
         3.14. log_assign (int)
         3.14. log_assign (int)
         3.15. cfgpkgcheck (int)
         3.15. cfgpkgcheck (int)
         3.16. reset_msgid (int)
         3.16. reset_msgid (int)
+        3.17. cfgtest (int)
 
 
    4. Functions
    4. Functions
 
 
@@ -183,6 +186,7 @@ Chapter 1. Admin Guide
    3.14. log_assign (int)
    3.14. log_assign (int)
    3.15. cfgpkgcheck (int)
    3.15. cfgpkgcheck (int)
    3.16. reset_msgid (int)
    3.16. reset_msgid (int)
+   3.17. cfgtest (int)
 
 
 3.1. cfgtrace (int)
 3.1. cfgtrace (int)
 
 
@@ -389,13 +393,26 @@ modparam("debugger", "cfgpkgcheck", 1)
 modparam("debugger", "reset_msgid", 1)
 modparam("debugger", "reset_msgid", 1)
 ...
 ...
 
 
+3.17. cfgtest (int)
+
+   Control whether the cfgt module is enabled or disabled at startup.
+   Module cfgt needs to be loaded before.
+
+   Default value is "0" (disabled).
+
+   Example 1.17. Set cfgtest parameter
+...
+loadmodule "cfgt.so"
+modparam("debugger", "cfgtest", 1)
+...
+
 4. Functions
 4. Functions
 
 
    4.1. dbg_breakpoint(mode)
    4.1. dbg_breakpoint(mode)
    4.2. dbg_pv_dump([mask] [, level])
    4.2. dbg_pv_dump([mask] [, level])
    4.3. dbg_sip_msg([log_level], [facility])
    4.3. dbg_sip_msg([log_level], [facility])
 
 
-4.1. dbg_breakpoint(mode)
+4.1.  dbg_breakpoint(mode)
 
 
    Anchor a breakpoint at the current line of the config (the one on which
    Anchor a breakpoint at the current line of the config (the one on which
    this function is called). The 'mode' specifies whether the breakpoint
    this function is called). The 'mode' specifies whether the breakpoint
@@ -404,13 +421,13 @@ modparam("debugger", "reset_msgid", 1)
    Note that this version of the module does not export this anchors to
    Note that this version of the module does not export this anchors to
    RPC for interactive debugging (temporarily disabled).
    RPC for interactive debugging (temporarily disabled).
 
 
-   Example 1.17. dbg_breakpoint usage
+   Example 1.18. dbg_breakpoint usage
 ...
 ...
 if($si=="10.0.0.10")
 if($si=="10.0.0.10")
         dbg_breakpoint("1");
         dbg_breakpoint("1");
 ...
 ...
 
 
-4.2. dbg_pv_dump([mask] [, level])
+4.2.  dbg_pv_dump([mask] [, level])
 
 
    Prints the content of pv_cache on json format. Defaults are mask=31 and
    Prints the content of pv_cache on json format. Defaults are mask=31 and
    level = "L_DBG"
    level = "L_DBG"
@@ -432,7 +449,7 @@ if($si=="10.0.0.10")
      * L_INFO - log level 2
      * L_INFO - log level 2
      * L_DBG - log level 3
      * L_DBG - log level 3
 
 
-   Example 1.18. dbg_pv_dump usage
+   Example 1.19. dbg_pv_dump usage
 ...
 ...
 $var(temp) = 1;
 $var(temp) = 1;
 $avp(s:more_avp) = 2;
 $avp(s:more_avp) = 2;
@@ -455,7 +472,7 @@ vp(x)":[{"different":["foo"]},{"other":[2,1],"more":["hi","bye"]}],"$T_branch_id
 x":0,"$var(empty)":0}
 x":0,"$var(empty)":0}
  ...
  ...
 
 
-4.3. dbg_sip_msg([log_level], [facility])
+4.3.  dbg_sip_msg([log_level], [facility])
 
 
    Prints how the sip message would look like if it would be sent out at
    Prints how the sip message would look like if it would be sent out at
    that point in the config(i.e. if the current lump lists would have been
    that point in the config(i.e. if the current lump lists would have been
@@ -475,7 +492,7 @@ x":0,"$var(empty)":0}
    force the lump application using msg_apply_changes() function from
    force the lump application using msg_apply_changes() function from
    textopsx module.
    textopsx module.
 
 
-   Example 1.19. dbg_sip_msg usage
+   Example 1.20. dbg_sip_msg usage
 ...
 ...
     dbg_sip_msg();
     dbg_sip_msg();
     dbg_sip_msg("L_ERR");
     dbg_sip_msg("L_ERR");
@@ -512,7 +529,7 @@ P-Hint: My hint
    5.4. dbg.mod_level
    5.4. dbg.mod_level
    5.5. dbg.reset_msgid
    5.5. dbg.reset_msgid
 
 
-5.1. dbg.ls
+5.1.  dbg.ls
 
 
    List Kamailio processes with info related to interactive debugging.
    List Kamailio processes with info related to interactive debugging.
 
 
@@ -526,7 +543,7 @@ P-Hint: My hint
                 dbg.ls
                 dbg.ls
                 dbg.ls 1234
                 dbg.ls 1234
 
 
-5.2. dbg.trace
+5.2.  dbg.trace
 
 
    Control config script running trace.
    Control config script running trace.
 
 
@@ -543,7 +560,7 @@ P-Hint: My hint
                 dbg.trace off
                 dbg.trace off
                 dbg.trace on 1234
                 dbg.trace on 1234
 
 
-5.3. dbg.bp
+5.3.  dbg.bp
 
 
    Control breakpoints and config execution.
    Control breakpoints and config execution.
 
 
@@ -581,7 +598,7 @@ P-Hint: My hint
                 dbg.bp eval 1234 $fu
                 dbg.bp eval 1234 $fu
                 dbg.bp move 1234
                 dbg.bp move 1234
 
 
-5.4. dbg.mod_level
+5.4.  dbg.mod_level
 
 
    Specify module log level.
    Specify module log level.
 
 
@@ -595,7 +612,7 @@ P-Hint: My hint
                 dbg.mod_level core 3
                 dbg.mod_level core 3
                 dbg.mod_level tm 3
                 dbg.mod_level tm 3
 
 
-5.5. dbg.reset_msgid
+5.5.  dbg.reset_msgid
 
 
    Resets the message sequence ($mi). Internally there is no real change.
    Resets the message sequence ($mi). Internally there is no real change.
    This can be useful for unit test cases in order to be able to replicate
    This can be useful for unit test cases in order to be able to replicate

+ 19 - 0
modules/debugger/debugger_api.c

@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
+#include "../cfgt/cfgt.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../events.h"
 #include "../../events.h"
 #include "../../locking.h"
 #include "../../locking.h"
@@ -70,6 +71,7 @@ str *dbg_get_state_name(int t)
 #define DBG_CFGTRACE_ON	(1<<0)
 #define DBG_CFGTRACE_ON	(1<<0)
 #define DBG_ABKPOINT_ON	(1<<1)
 #define DBG_ABKPOINT_ON	(1<<1)
 #define DBG_LBKPOINT_ON	(1<<2)
 #define DBG_LBKPOINT_ON	(1<<2)
+#define DBG_CFGTEST_ON	(1<<3)
 
 
 static str _dbg_status_list[] = {
 static str _dbg_status_list[] = {
 	str_init("cfgtrace-on"),
 	str_init("cfgtrace-on"),
@@ -78,6 +80,8 @@ static str _dbg_status_list[] = {
 	str_init("abkpoint-off"),
 	str_init("abkpoint-off"),
 	str_init("lbkpoint-on"),
 	str_init("lbkpoint-on"),
 	str_init("lbkpoint-off"),
 	str_init("lbkpoint-off"),
+	str_init("cfgtest-on"),
+	str_init("cfgtest-off"),
 	{0, 0}
 	{0, 0}
 };
 };
 
 
@@ -89,6 +93,8 @@ str *dbg_get_status_name(int t)
 		return &_dbg_status_list[2];
 		return &_dbg_status_list[2];
 	if(t&DBG_LBKPOINT_ON)
 	if(t&DBG_LBKPOINT_ON)
 		return &_dbg_status_list[4];
 		return &_dbg_status_list[4];
+	if(t&DBG_CFGTEST_ON)
+		return &_dbg_status_list[6];
 
 
 	return &_dbg_state_list[0];
 	return &_dbg_state_list[0];
 }
 }
@@ -188,6 +194,12 @@ int _dbg_step_loops = 200;
  */
  */
 int _dbg_reset_msgid = 0;
 int _dbg_reset_msgid = 0;
 
 
+/**
+ * disabled by default
+ */
+int _dbg_cfgtest = 0;
+cfgt_api_t _dbg_cfgt;
+
 /**
 /**
  *
  *
  */
  */
@@ -356,6 +368,11 @@ int dbg_cfg_trace(void *data)
 				);
 				);
 		}
 		}
 	}
 	}
+	if(_dbg_pid_list[process_no].set&DBG_CFGTEST_ON)
+	{
+		if(_dbg_cfgt.cfgt_process_route(msg, a)<0)
+				LM_ERR("Error processing route\n");
+	}
 	if(!(_dbg_pid_list[process_no].set&DBG_ABKPOINT_ON))
 	if(!(_dbg_pid_list[process_no].set&DBG_ABKPOINT_ON))
 	{
 	{
 		/* no breakpoints to be considered */
 		/* no breakpoints to be considered */
@@ -590,6 +607,8 @@ int dbg_init_mypid(void)
 		_dbg_pid_list[process_no].set |= DBG_ABKPOINT_ON;
 		_dbg_pid_list[process_no].set |= DBG_ABKPOINT_ON;
 	if(_dbg_cfgtrace==1)
 	if(_dbg_cfgtrace==1)
 		_dbg_pid_list[process_no].set |= DBG_CFGTRACE_ON;
 		_dbg_pid_list[process_no].set |= DBG_CFGTRACE_ON;
+	if(_dbg_cfgtest==1)
+		_dbg_pid_list[process_no].set |= DBG_CFGTEST_ON;
 	if(_dbg_reset_msgid==1)
 	if(_dbg_reset_msgid==1)
 	{
 	{
 		LM_DBG("[%d] create locks\n", process_no);
 		LM_DBG("[%d] create locks\n", process_no);

+ 21 - 0
modules/debugger/debugger_mod.c

@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 
 
+#include "../cfgt/cfgt.h"
 #include "../../sr_module.h"
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../ut.h"
 #include "../../ut.h"
@@ -71,6 +72,10 @@ extern char *_dbg_cfgtrace_lname;
 extern int _dbg_step_usleep;
 extern int _dbg_step_usleep;
 extern int _dbg_step_loops;
 extern int _dbg_step_loops;
 extern int _dbg_reset_msgid;
 extern int _dbg_reset_msgid;
+extern int _dbg_cfgtest;
+
+/* cfgt api */
+extern cfgt_api_t _dbg_cfgt;
 
 
 static int _dbg_sip_msg_cline;
 static int _dbg_sip_msg_cline;
 static char * _dbg_cfgtrace_facility_str = 0;
 static char * _dbg_cfgtrace_facility_str = 0;
@@ -111,6 +116,7 @@ static param_export_t params[]={
 	{"mod_facility",      PARAM_STRING|USE_FUNC_PARAM, (void*)dbg_mod_facility_param},
 	{"mod_facility",      PARAM_STRING|USE_FUNC_PARAM, (void*)dbg_mod_facility_param},
 	{"reset_msgid",       INT_PARAM, &_dbg_reset_msgid},
 	{"reset_msgid",       INT_PARAM, &_dbg_reset_msgid},
 	{"cfgpkgcheck",       INT_PARAM, &_dbg_cfgpkgcheck},
 	{"cfgpkgcheck",       INT_PARAM, &_dbg_cfgpkgcheck},
+	{"cfgtest",           INT_PARAM, &_dbg_cfgtest},
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
@@ -136,6 +142,8 @@ struct module_exports exports = {
 static int mod_init(void)
 static int mod_init(void)
 {
 {
 	int fl;
 	int fl;
+	bind_cfgt_t bind_cfgt;
+
 	if (_dbg_cfgtrace_facility_str!=NULL)
 	if (_dbg_cfgtrace_facility_str!=NULL)
 	{
 	{
 		fl = str2facility(_dbg_cfgtrace_facility_str);
 		fl = str2facility(_dbg_cfgtrace_facility_str);
@@ -187,6 +195,19 @@ static int mod_init(void)
 			return -1;
 			return -1;
 		}
 		}
 	}
 	}
+	if(_dbg_cfgtest==1)
+	{
+		bind_cfgt = (bind_cfgt_t)find_export("cfgt_bind_cfgt", 1, 0);
+		if (!bind_cfgt) {
+			LM_ERR("can't find cfgt module\n");
+			return -1;
+		}
+
+		if (bind_cfgt(&_dbg_cfgt) < 0) {
+			return -1;
+		}
+		LM_INFO("bind to cfgt module\n");
+	}
 	return dbg_init_bp_list();
 	return dbg_init_bp_list();
 }
 }
 
 

+ 22 - 0
modules/debugger/doc/debugger_admin.xml

@@ -418,6 +418,28 @@ modparam("debugger", "reset_msgid", 1)
 	    </example>
 	    </example>
 	</section>
 	</section>
 
 
+	<section id="dbg.p.cfgtest">
+	    <title><varname>cfgtest</varname> (int)</title>
+	    <para>
+			Control whether the cfgt module is enabled or disabled
+			at startup. Module cfgt needs to be loaded before.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>0</quote> (disabled).
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>cfgtest</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+loadmodule "cfgt.so"
+modparam("debugger", "cfgtest", 1)
+...
+</programlisting>
+	    </example>
+	</section>
+
 	</section>
 	</section>
 
 
     <section>
     <section>