diff --git a/modules/cfgt/Makefile b/modules/cfgt/Makefile new file mode 100644 index 00000000000..7bfba621666 --- /dev/null +++ b/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 diff --git a/modules/cfgt/README b/modules/cfgt/README new file mode 100644 index 00000000000..a3be5ffac22 --- /dev/null +++ b/modules/cfgt/README @@ -0,0 +1,126 @@ +cfgt Module + +Victor Seva + + sipwise.com + +Edited by + +Victor Seva + + + + 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%") +... diff --git a/modules/cfgt/cfgt.c b/modules/cfgt/cfgt.c new file mode 100644 index 00000000000..256a259039a --- /dev/null +++ b/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; +} diff --git a/modules/cfgt/cfgt.h b/modules/cfgt/cfgt.h new file mode 100644 index 00000000000..f06a87e0c26 --- /dev/null +++ b/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 diff --git a/modules/cfgt/cfgt_int.c b/modules/cfgt/cfgt_int.c new file mode 100644 index 00000000000..f5e87dbe944 --- /dev/null +++ b/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 +#include + +#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; ihash.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; +} diff --git a/modules/cfgt/cfgt_int.h b/modules/cfgt/cfgt_int.h new file mode 100644 index 00000000000..3587264bcbd --- /dev/null +++ b/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 diff --git a/modules/cfgt/cfgt_json.c b/modules/cfgt/cfgt_json.c new file mode 100644 index 00000000000..b4c235924b4 --- /dev/null +++ b/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 + +#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&&ilen==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, "<>", avp->val.v.xavp); + break; + case SR_XTYPE_DATA: + result = snprintf(_pv_xavp_buf, 128, "<>", 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;ispec.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; +} \ No newline at end of file diff --git a/modules/cfgt/cfgt_json.h b/modules/cfgt/cfgt_json.h new file mode 100644 index 00000000000..7ab10b4df1c --- /dev/null +++ b/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 diff --git a/modules/cfgt/cfgt_mod.c b/modules/cfgt/cfgt_mod.c new file mode 100644 index 00000000000..6b5ad42877a --- /dev/null +++ b/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) +{ +} diff --git a/modules/cfgt/cfgt_mod.h b/modules/cfgt/cfgt_mod.h new file mode 100644 index 00000000000..1d8323d03e1 --- /dev/null +++ b/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 diff --git a/modules/cfgt/doc/Makefile b/modules/cfgt/doc/Makefile new file mode 100644 index 00000000000..5ff9aaec00f --- /dev/null +++ b/modules/cfgt/doc/Makefile @@ -0,0 +1,4 @@ +docs = cfgt.xml + +docbook_dir = ../../../docbook +include $(docbook_dir)/Makefile.module diff --git a/modules/cfgt/doc/cfgt.xml b/modules/cfgt/doc/cfgt.xml new file mode 100644 index 00000000000..dbbdbb8952c --- /dev/null +++ b/modules/cfgt/doc/cfgt.xml @@ -0,0 +1,36 @@ + + + +%docentities; + +]> + + + + cfgt Module + &kamailioname; + + + Victor + Seva + sipwise.com + + + Victor + Seva + linuxmaniac@torreviejawireless.org + + + + 2015 + Victor Seva (sipwise.com) + + + + + + + diff --git a/modules/cfgt/doc/cfgt_admin.xml b/modules/cfgt/doc/cfgt_admin.xml new file mode 100644 index 00000000000..07b8ea4f66f --- /dev/null +++ b/modules/cfgt/doc/cfgt_admin.xml @@ -0,0 +1,158 @@ + + + +%docentities; + +]> + + + + + + &adminguide; + +
+ Overview + + This module provides a report of the way &kamailioname; 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. + +
+
+ Dependencies +
+ &kamailio; Modules + + The following modules must be loaded before this module: + + + + None. + + + + +
+
+ External Libraries or Applications + + The following libraries or applications must be installed before running + &kamailio; with this module loaded: + + + + None. + + + + +
+
+
+ Parameters +
+ <varname>basedir</varname> (string) + + Control where the config reports should be stored. The dir must + exists and &kamailioname; must have perms to write on it. + + + + Default value is /tmp. + + + + Set <varname>cfgtrace</varname> parameter + +... +modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest") +... + + +
+ +
+ <varname>mask</varname> (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). + + + + Set <varname>mask</varname> parameter + +... +# dump xavp(8) and avp(4) vars +modparam("cfgt", "mask", 12) +... + + +
+ +
+ <varname>callid_prefix</varname> (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). + + + + Set <varname>callid_prefix</varname> parameter + +... +# using '%' as delimiter +modparam("cfgt", "callid_prefix", "TEST-ID%") +... + + +
+ +
+ + +
diff --git a/modules/debugger/README b/modules/debugger/README index 609bc3ca57c..7ef1ef1c349 100644 --- a/modules/debugger/README +++ b/modules/debugger/README @@ -41,6 +41,7 @@ Daniel-Constantin Mierla 3.14. log_assign (int) 3.15. cfgpkgcheck (int) 3.16. reset_msgid (int) + 3.17. cfgtest (int) 4. Functions @@ -76,9 +77,10 @@ Daniel-Constantin Mierla 1.14. Set log_assign parameter 1.15. Set cfgpkgcheck 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 @@ -108,6 +110,7 @@ Chapter 1. Admin Guide 3.14. log_assign (int) 3.15. cfgpkgcheck (int) 3.16. reset_msgid (int) + 3.17. cfgtest (int) 4. Functions @@ -183,6 +186,7 @@ Chapter 1. Admin Guide 3.14. log_assign (int) 3.15. cfgpkgcheck (int) 3.16. reset_msgid (int) + 3.17. cfgtest (int) 3.1. cfgtrace (int) @@ -389,13 +393,26 @@ modparam("debugger", "cfgpkgcheck", 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.1. dbg_breakpoint(mode) 4.2. dbg_pv_dump([mask] [, level]) 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 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 RPC for interactive debugging (temporarily disabled). - Example 1.17. dbg_breakpoint usage + Example 1.18. dbg_breakpoint usage ... if($si=="10.0.0.10") 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 level = "L_DBG" @@ -432,7 +449,7 @@ if($si=="10.0.0.10") * L_INFO - log level 2 * L_DBG - log level 3 - Example 1.18. dbg_pv_dump usage + Example 1.19. dbg_pv_dump usage ... $var(temp) = 1; $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} ... -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 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 textopsx module. - Example 1.19. dbg_sip_msg usage + Example 1.20. dbg_sip_msg usage ... dbg_sip_msg(); dbg_sip_msg("L_ERR"); @@ -512,7 +529,7 @@ P-Hint: My hint 5.4. dbg.mod_level 5.5. dbg.reset_msgid -5.1. dbg.ls +5.1. dbg.ls List Kamailio processes with info related to interactive debugging. @@ -526,7 +543,7 @@ P-Hint: My hint dbg.ls dbg.ls 1234 -5.2. dbg.trace +5.2. dbg.trace Control config script running trace. @@ -543,7 +560,7 @@ P-Hint: My hint dbg.trace off dbg.trace on 1234 -5.3. dbg.bp +5.3. dbg.bp Control breakpoints and config execution. @@ -581,7 +598,7 @@ P-Hint: My hint dbg.bp eval 1234 $fu dbg.bp move 1234 -5.4. dbg.mod_level +5.4. dbg.mod_level Specify module log level. @@ -595,7 +612,7 @@ P-Hint: My hint dbg.mod_level core 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. This can be useful for unit test cases in order to be able to replicate diff --git a/modules/debugger/debugger_api.c b/modules/debugger/debugger_api.c index b1e7fe1aa4a..107fa09b4f6 100644 --- a/modules/debugger/debugger_api.c +++ b/modules/debugger/debugger_api.c @@ -26,6 +26,7 @@ #include #include +#include "../cfgt/cfgt.h" #include "../../dprint.h" #include "../../events.h" #include "../../locking.h" @@ -70,6 +71,7 @@ str *dbg_get_state_name(int t) #define DBG_CFGTRACE_ON (1<<0) #define DBG_ABKPOINT_ON (1<<1) #define DBG_LBKPOINT_ON (1<<2) +#define DBG_CFGTEST_ON (1<<3) static str _dbg_status_list[] = { str_init("cfgtrace-on"), @@ -78,6 +80,8 @@ static str _dbg_status_list[] = { str_init("abkpoint-off"), str_init("lbkpoint-on"), str_init("lbkpoint-off"), + str_init("cfgtest-on"), + str_init("cfgtest-off"), {0, 0} }; @@ -89,6 +93,8 @@ str *dbg_get_status_name(int t) return &_dbg_status_list[2]; if(t&DBG_LBKPOINT_ON) return &_dbg_status_list[4]; + if(t&DBG_CFGTEST_ON) + return &_dbg_status_list[6]; return &_dbg_state_list[0]; } @@ -188,6 +194,12 @@ int _dbg_step_loops = 200; */ 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)) { /* no breakpoints to be considered */ @@ -590,6 +607,8 @@ int dbg_init_mypid(void) _dbg_pid_list[process_no].set |= DBG_ABKPOINT_ON; if(_dbg_cfgtrace==1) _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) { LM_DBG("[%d] create locks\n", process_no); diff --git a/modules/debugger/debugger_mod.c b/modules/debugger/debugger_mod.c index d0a35aff82f..44c58650940 100644 --- a/modules/debugger/debugger_mod.c +++ b/modules/debugger/debugger_mod.c @@ -27,6 +27,7 @@ #include #include +#include "../cfgt/cfgt.h" #include "../../sr_module.h" #include "../../dprint.h" #include "../../ut.h" @@ -71,6 +72,10 @@ extern char *_dbg_cfgtrace_lname; extern int _dbg_step_usleep; extern int _dbg_step_loops; extern int _dbg_reset_msgid; +extern int _dbg_cfgtest; + +/* cfgt api */ +extern cfgt_api_t _dbg_cfgt; static int _dbg_sip_msg_cline; 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}, {"reset_msgid", INT_PARAM, &_dbg_reset_msgid}, {"cfgpkgcheck", INT_PARAM, &_dbg_cfgpkgcheck}, + {"cfgtest", INT_PARAM, &_dbg_cfgtest}, {0, 0, 0} }; @@ -136,6 +142,8 @@ struct module_exports exports = { static int mod_init(void) { int fl; + bind_cfgt_t bind_cfgt; + if (_dbg_cfgtrace_facility_str!=NULL) { fl = str2facility(_dbg_cfgtrace_facility_str); @@ -187,6 +195,19 @@ static int mod_init(void) 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(); } diff --git a/modules/debugger/doc/debugger_admin.xml b/modules/debugger/doc/debugger_admin.xml index f003fb5f80f..cddfc26746f 100644 --- a/modules/debugger/doc/debugger_admin.xml +++ b/modules/debugger/doc/debugger_admin.xml @@ -418,6 +418,28 @@ modparam("debugger", "reset_msgid", 1) +
+ <varname>cfgtest</varname> (int) + + Control whether the cfgt module is enabled or disabled + at startup. Module cfgt needs to be loaded before. + + + + Default value is 0 (disabled). + + + + Set <varname>cfgtest</varname> parameter + +... +loadmodule "cfgt.so" +modparam("debugger", "cfgtest", 1) +... + + +
+