sr_module.c
3c5a8364
 /*
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
6a0f4382
  * This file is part of Kamailio, a free SIP server.
7dd0b342
  *
6a0f4382
  * Kamailio is free software; you can redistribute it and/or modify
7dd0b342
  * 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
  *
6a0f4382
  * Kamailio is distributed in the hope that it will be useful,
7dd0b342
  * 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.
  *
fb851d7e
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
3bf76e49
  */
a7524985
 
 /**
  * @file
6a0f4382
  * @brief Kamailio core :: modules loading, structures declarations and utilities
a5b499a4
  * @ingroup core
a7524985
  * Module: \ref core
1d0661db
  */
7dd0b342
 
a7524985
 
3bf76e49
 #include "sr_module.h"
0b52bcff
 #include "mod_fix.h"
3bf76e49
 #include "dprint.h"
404073d3
 #include "error.h"
e3dccdc9
 #include "mem/mem.h"
300b0f50
 #include "core_cmd.h"
bb6575cd
 #include "ut.h"
d0ce0ebd
 #include "re.h"
f141bc93
 #include "route_struct.h"
6d35d70e
 #include "flags.h"
b91364c6
 #include "trim.h"
2f292107
 #include "pvapi.h"
f4194f69
 #include "globals.h"
5099fd64
 #include "rpc_lookup.h"
b25e31aa
 #include "sr_compat.h"
14525efb
 #include "ppcfg.h"
d5a22ca8
 #include "async_task.h"
3bf76e49
 
f4194f69
 #include <sys/stat.h>
f6712967
 #include <regex.h>
3bf76e49
 #include <dlfcn.h>
 #include <strings.h>
 #include <stdlib.h>
e22bbdb8
 #include <string.h>
b2383262
 #include <stddef.h> /* for offsetof */
3bf76e49
 
 
031e278e
 struct sr_module* modules=0;
3bf76e49
 
577585de
 /*We need to define this symbol on Solaris becuase libcurl relies on libnspr which looks for this symbol.
   If it is not defined, dynamic module loading (dlsym) fails */
 #ifdef __OS_solaris
 	int nspr_use_zone_allocator = 0;
 #endif
 
 
30449150
 #ifdef STATIC_EXEC
2873d384
 	extern struct module_exports exec_exports;
30449150
 #endif
404073d3
 #ifdef STATIC_TM
2873d384
 	extern struct module_exports tm_exports;
404073d3
 #endif
57c9e572
 
6eb22d94
 #ifdef STATIC_MAXFWD
2873d384
 	extern struct module_exports maxfwd_exports;
6eb22d94
 #endif
57c9e572
 
192ac55b
 #ifdef STATIC_AUTH
2873d384
 	extern struct module_exports auth_exports;
192ac55b
 #endif
57c9e572
 
192ac55b
 #ifdef STATIC_RR
2873d384
 	extern struct module_exports rr_exports;
192ac55b
 #endif
57c9e572
 
192ac55b
 #ifdef STATIC_USRLOC
2873d384
 	extern struct module_exports usrloc_exports;
192ac55b
 #endif
404073d3
 
a50f3f89
 #ifdef STATIC_SL
2873d384
 	extern struct module_exports sl_exports;
a50f3f89
 #endif
 
b4c42f7b
 #ifndef offsetof
 #warning "use null pointer dereference for offsetof"
 #define offsetof(st, m) \
 		((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
 #endif
404073d3
 
55faef7d
 int mod_response_cbk_no=0;
 response_function* mod_response_cbks=0;
 
52d33940
 /* number of usec to wait before initializing a module */
 static unsigned int modinit_delay = 0;
 
 unsigned int set_modinit_delay(unsigned int v)
 {
 	unsigned int r;
 	r =  modinit_delay;
 	modinit_delay = v;
 	return r;
 }
 
37db0ca7
 /**
df1958c0
  * if bit 1 set, SIP worker processes handle RPC commands as well
  * if bit 2 set, RPC worker processes handle SIP commands as well
37db0ca7
  */
 static int child_sip_rpc_mode = 0;
 
df1958c0
 #define CHILD_SIP_RPC	1<<0
 #define CHILD_RPC_SIP	1<<1
 
 void set_child_sip_rpc_mode(void)
37db0ca7
 {
df1958c0
 	child_sip_rpc_mode |= CHILD_SIP_RPC;
37db0ca7
 }
 
df1958c0
 void set_child_rpc_sip_mode(void)
37db0ca7
 {
df1958c0
 	child_sip_rpc_mode |= CHILD_RPC_SIP;
37db0ca7
 }
 
 int is_rpc_worker(int rank)
 {
df1958c0
 	if(rank==PROC_RPC
 			|| (rank>PROC_MAIN && (child_sip_rpc_mode&CHILD_SIP_RPC)!=0))
 		return 1;
 	return 0;
 }
 
 int is_sip_worker(int rank)
 {
 	if(rank>PROC_MAIN
 			|| ((rank==PROC_RPC || rank==PROC_NOCHLDINIT)
 					&& (child_sip_rpc_mode&CHILD_RPC_SIP)!=0))
37db0ca7
 		return 1;
 	return 0;
 }
 
404073d3
 /* initializes statically built (compiled in) modules*/
31309a3a
 int register_builtin_modules()
404073d3
 {
e22bbdb8
 	int ret;
 
 	ret=0;
57c9e572
 #ifdef STATIC_TM
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &tm_exports,"built-in", 0);
57c9e572
 	if (ret<0) return ret;
 #endif
192ac55b
 
e8df4b11
 #ifdef STATIC_EXEC
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &exec_exports,"built-in", 0);
30449150
 	if (ret<0) return ret;
 #endif
 
57c9e572
 #ifdef STATIC_MAXFWD
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &maxfwd_exports, "built-in", 0);
57c9e572
 	if (ret<0) return ret;
192ac55b
 #endif
 
57c9e572
 #ifdef STATIC_AUTH
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &auth_exports, "built-in", 0);
57c9e572
 	if (ret<0) return ret;
 #endif
fb851d7e
 
192ac55b
 #ifdef STATIC_RR
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &rr_exports, "built-in", 0);
57c9e572
 	if (ret<0) return ret;
192ac55b
 #endif
fb851d7e
 
192ac55b
 #ifdef STATIC_USRLOC
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &usrloc_exports, "built-in", 0);
57c9e572
 	if (ret<0) return ret;
192ac55b
 #endif
a50f3f89
 
 #ifdef STATIC_SL
2873d384
 	ret=register_module(MODULE_INTERFACE_VER, &sl_exports, "built-in", 0);
a50f3f89
 	if (ret<0) return ret;
 #endif
fb851d7e
 
e22bbdb8
 	return ret;
404073d3
 }
 
 
 
dee21fff
 /** convert cmd exports to current format.
   * @param ver - module interface versions (0 == ser, 1 == kam).
   * @param src - null terminated array of cmd exports
   *              (either ser_cmd_export_t or kam_cmd_export_t, depending
   *               on ver).
   * @param mod - pointer to module exports structure.
   * @return - pkg_malloc'ed null terminated sr_cmd_export_v31_t array with
   *           the converted cmd exports  or 0 on error.
   */
 static sr31_cmd_export_t* sr_cmd_exports_convert(unsigned ver,
 													void* src, void* mod)
 {
 	int i, n;
 	ser_cmd_export_t* ser_cmd;
 	kam_cmd_export_t* kam_cmd;
 	sr31_cmd_export_t* ret;
 	
 	ser_cmd = 0;
 	kam_cmd = 0;
 	ret = 0;
 	n = 0;
 	/* count the number of elements */
 	if (ver == 0) {
 		ser_cmd = src;
 		for (; ser_cmd[n].name; n++);
 	} else if (ver == 1) {
 		kam_cmd = src;
 		for (; kam_cmd[n].name; n++);
 	} else goto error; /* unknown interface version */
 	/* alloc & init new array */
 	ret = pkg_malloc(sizeof(*ret)*(n+1));
 	memset(ret, 0, sizeof(*ret)*(n+1));
 	/* convert/copy */
 	for (i=0; i < n; i++) {
 		if (ver == 0) {
 			ret[i].name = ser_cmd[i].name;
 			ret[i].function = ser_cmd[i].function;
 			ret[i].param_no = ser_cmd[i].param_no;
 			ret[i].fixup = ser_cmd[i].fixup;
 			ret[i].free_fixup = 0; /* no present in ser  <= 2.1 */
 			ret[i].flags = ser_cmd[i].flags;
 		} else {
 			ret[i].name = kam_cmd[i].name;
 			ret[i].function = kam_cmd[i].function;
 			ret[i].param_no = kam_cmd[i].param_no;
 			ret[i].fixup = kam_cmd[i].fixup;
 			ret[i].free_fixup = kam_cmd[i].free_fixup;
 			ret[i].flags = kam_cmd[i].flags;
 		}
 		/* 3.1+ specific stuff */
 		ret[i].fixup_flags = 0;
 		ret[i].module_exports = mod;
d709a720
 		/* fill known free fixups */
 		if (ret[i].fixup && ret[i].free_fixup == 0)
 			ret[i].free_fixup = get_fixup_free(ret[i].fixup);
dee21fff
 	}
 	return ret;
 error:
 	return 0;
 }
 
 
 
404073d3
 /* registers a module,  register_f= module register  functions
  * returns <0 on error, 0 on success */
2873d384
 static int register_module(unsigned ver, union module_exports_u* e,
 					char* path, void* handle)
404073d3
 {
5099fd64
 	int ret, i;
e22bbdb8
 	struct sr_module* mod;
14525efb
 	char defmod[64];
fb851d7e
 
404073d3
 	ret=-1;
31309a3a
 
404073d3
 	/* add module to the list */
e3dccdc9
 	if ((mod=pkg_malloc(sizeof(struct sr_module)))==0){
153ab772
 		LM_ERR("memory allocation failure\n");
404073d3
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	memset(mod,0, sizeof(struct sr_module));
 	mod->path=path;
 	mod->handle=handle;
dee21fff
 	mod->orig_mod_interface_ver=ver;
 	/* convert exports to sr31 format */
 	if (ver == 0) {
 		/* ser <= 3.0 */
 		mod->exports.name = e->v0.name;
 		if (e->v0.cmds) {
 			mod->exports.cmds = sr_cmd_exports_convert(ver, e->v0.cmds, mod);
 			if (mod->exports.cmds == 0) {
153ab772
 				LM_ERR("failed to convert module command exports to 3.1 format"
dee21fff
 						" for module \"%s\" (%s), interface version %d\n",
 						mod->exports.name, mod->path, ver);
 				ret = E_UNSPEC;
 				goto error;
 			}
 		}
 		mod->exports.params = e->v0.params;
 		mod->exports.init_f = e->v0.init_f;
 		mod->exports.response_f = e->v0.response_f;
 		mod->exports.destroy_f = e->v0.destroy_f;
 		mod->exports.onbreak_f = e->v0.onbreak_f;
 		mod->exports.init_child_f = e->v0.init_child_f;
 		mod->exports.dlflags = 0; /* not used in ser <= 3.0 */
 		mod->exports.rpc_methods = e->v0.rpc_methods;
 		/* the rest are 0, not used in ser */
 	} else if (ver == 1) {
 		/* kamailio <= 3.0 */
 		mod->exports.name = e->v1.name;
 		if (e->v1.cmds) {
 			mod->exports.cmds = sr_cmd_exports_convert(ver, e->v1.cmds, mod);
 			if (mod->exports.cmds == 0) {
153ab772
 				LM_ERR("failed to convert module command exports to 3.1 format"
dee21fff
 						" for module \"%s\" (%s), interface version %d\n",
 						mod->exports.name, mod->path, ver);
 				ret = E_UNSPEC;
 				goto error;
 			}
 		}
 		mod->exports.params = e->v1.params;
 		mod->exports.init_f = e->v1.init_f;
 		mod->exports.response_f = e->v1.response_f;
 		mod->exports.destroy_f = e->v1.destroy_f;
 		mod->exports.onbreak_f = 0; /* not used in k <= 3.0 */
 		mod->exports.init_child_f = e->v1.init_child_f;
 		mod->exports.dlflags = e->v1.dlflags;
 		mod->exports.rpc_methods = 0; /* not used in k <= 3.0 */
 		mod->exports.stats = e->v1.stats;
 		mod->exports.mi_cmds = e->v1.mi_cmds;
 		mod->exports.items = e->v1.items;
 		mod->exports.procs = e->v1.procs;
 	} else {
153ab772
 		LM_ERR("unsupported module interface version %d\n", ver);
dee21fff
 		ret = E_UNSPEC;
 		goto error;
 	}
d79388b3
 
dee21fff
 	if (mod->exports.items) {
5099fd64
 		/* register module pseudo-variables for kamailio modules */
dee21fff
 		LM_DBG("register PV from: %s\n", mod->exports.name);
 		if (register_pvars_mod(mod->exports.name, mod->exports.items)!=0) {
 			LM_ERR("failed to register pseudo-variables for module %s (%s)\n",
 				mod->exports.name, path);
 			ret = E_UNSPEC;
 			goto error;
d79388b3
 		}
dee21fff
 	}
 	if (mod->exports.rpc_methods){
5099fd64
 		/* register rpcs for ser modules */
dee21fff
 		i=rpc_register_array(mod->exports.rpc_methods);
5099fd64
 		if (i<0){
153ab772
 			LM_ERR("failed to register RPCs for module %s (%s)\n",
dee21fff
 					mod->exports.name, path);
 			ret = E_UNSPEC;
5099fd64
 			goto error;
 		}else if (i>0){
153ab772
 			LM_ERR("%d duplicate RPCs name detected while registering RPCs"
dee21fff
 					" declared in module %s (%s)\n",
 					i, mod->exports.name, path);
 			ret = E_UNSPEC;
5099fd64
 			goto error;
 		}
 		/* i==0 => success */
d79388b3
 	}
 
14525efb
 	/* add cfg define for each module: MOD_modulename */
 	if(strlen(mod->exports.name)>=60) {
 		LM_ERR("too long module name: %s\n", mod->exports.name);
 		goto error;
 	}
 	strcpy(defmod, "MOD_");
 	strcat(defmod, mod->exports.name);
 	pp_define_set_type(0);
 	if(pp_define(strlen(defmod), defmod)<0) {
 		LM_ERR("unable to set cfg define for module: %s\n",
 				mod->exports.name);
 		goto error;
 	}
 
dee21fff
 	/* link module in the list */
 	mod->next=modules;
 	modules=mod;
404073d3
 	return 0;
 error:
dee21fff
 	if (mod)
 		pkg_free(mod);
404073d3
 	return ret;
 }
 
2dcb8b67
 #ifndef DLSYM_PREFIX
 /* define it to null */
 #define DLSYM_PREFIX
 #endif
 
 static inline int version_control(void *handle, char *path)
 {
 	char **m_ver;
dd0e65a8
 	char **m_flags;
2dcb8b67
 	char* error;
 
 	m_ver=(char **)dlsym(handle, DLSYM_PREFIX "module_version");
 	if ((error=(char *)dlerror())!=0) {
153ab772
 		LM_ERR("no version info in module <%s>: %s\n", path, error);
2dcb8b67
 		return 0;
 	}
dd0e65a8
 	m_flags=(char **)dlsym(handle, DLSYM_PREFIX "module_flags");
 	if ((error=(char *)dlerror())!=0) {
153ab772
 		LM_ERR("no compile flags info in module <%s>: %s\n", path, error);
dd0e65a8
 		return 0;
 	}
2dcb8b67
 	if (!m_ver || !(*m_ver)) {
153ab772
 		LM_ERR("no version in module <%s>\n", path );
2dcb8b67
 		return 0;
 	}
dd0e65a8
 	if (!m_flags || !(*m_flags)) {
153ab772
 		LM_ERR("no compile flags in module <%s>\n", path );
dd0e65a8
 		return 0;
 	}
fb851d7e
 
dd0e65a8
 	if (strcmp(SER_FULL_VERSION, *m_ver)==0){
 		if (strcmp(SER_COMPILE_FLAGS, *m_flags)==0)
 			return 1;
 		else {
153ab772
 			LM_ERR("module compile flags mismatch for %s "
dd0e65a8
 						" \ncore: %s \nmodule: %s\n",
 						path, SER_COMPILE_FLAGS, *m_flags);
 			return 0;
 		}
 	}
153ab772
 	LM_ERR("module version mismatch for %s; "
dd0e65a8
 		"core: %s; module: %s\n", path, SER_FULL_VERSION, *m_ver );
2dcb8b67
 	return 0;
 }
3bf76e49
 
3c5a8364
 /**
  * \brief load a sr module
  * 
c9c181f8
  * tries to load the module specified by mod_path.
  * If mod_path is 'modname' or 'modname.so' then
3c5a8364
  *  \<MODS_DIR\>/\<modname\>.so will be tried and if this fails
  *  \<MODS_DIR\>/\<modname\>/\<modname\>.so
c9c181f8
  * If mod_path contain a '/' it is assumed to be the 
  * path to the module and tried first. If fails and mod_path is not
  * absolute path (not starting with '/') then will try:
3c5a8364
  * \<MODS_DIR\>/mod_path
  * @param mod_path path or module name
ea31876f
  * @return 0 on success , <0 on error
  */
fdbb341c
 int load_module(char* mod_path)
3bf76e49
 {
 	void* handle;
 	char* error;
ea31876f
 	mod_register_function mr;
2873d384
 	union module_exports_u* exp;
 	unsigned* mod_if_ver;
404073d3
 	struct sr_module* t;
f4194f69
 	struct stat stat_buf;
b27dfba5
 	str modname;
fdbb341c
 	char* mdir;
 	char* nxt_mdir;
 	char* path;
 	int mdir_len;
f4194f69
 	int len;
ea31876f
 	int dlflags;
 	int new_dlflags;
 	int retries;
b27dfba5
 	int path_type;
fb851d7e
 
087d0976
 #ifndef RTLD_NOW
e61f4ba3
 /* for openbsd */
087d0976
 #define RTLD_NOW DL_LAZY
 #endif
fdbb341c
 	path=mod_path;
b27dfba5
 	path_type = 0;
 	modname.s = path;
 	modname.len = strlen(mod_path);
 	if(modname.len>3 && strcmp(modname.s+modname.len-3, ".so")==0) {
 		path_type = 1;
 		modname.len -= 3;
 	}
 	if (!strchr(path, '/'))
 		path_type |= 2;
 	if((path_type&2) || path[0] != '/') {
f4194f69
 		/* module name was given, we try to construct the path */
fdbb341c
 		mdir=mods_dir; /* search path */
 		do{
 			nxt_mdir=strchr(mdir, ':');
 			if (nxt_mdir) mdir_len=(int)(nxt_mdir-mdir);
 			else mdir_len=strlen(mdir);
 			
b27dfba5
 			if(path_type&2) {
 				/* try path <MODS_DIR>/<modname>.so */
 				path = (char*)pkg_malloc(mdir_len + 1 /* "/" */ +
 									modname.len + 3 /* ".so" */ + 1);
fdbb341c
 				if (path==0) goto error;
 				memcpy(path, mdir, mdir_len);
 				len = mdir_len;
b27dfba5
 				if (len != 0 && path[len - 1] != '/'){
fdbb341c
 					path[len]='/';
 					len++;
 				}
 				path[len]=0;
b27dfba5
 				strcat(path, modname.s);
 				if(!(path_type&1))
 					strcat(path, ".so");
fdbb341c
 
 				if (stat(path, &stat_buf) == -1) {
c6a1ba6d
 					LM_DBG("module file not found <%s>\n", path);
fdbb341c
 					pkg_free(path);
b27dfba5
 
 					/* try path <MODS_DIR>/<modname>/<modname>.so */
 					path = (char*)pkg_malloc(
 						mdir_len + 1 /* "/" */ +
 						modname.len + 1 /* "/" */ +
 						modname.len + 3 /* ".so" */ + 1);
 					if (path==0) goto error;
 					memcpy(path, mdir, mdir_len);
 					len = mdir_len;
 					if (len != 0 && path[len - 1] != '/') {
 						path[len]='/';
 						len++;
 					}
 					path[len]=0;
 					strncat(path, modname.s, modname.len);
 					strcat(path, "/");
 					strcat(path, modname.s);
 					if(!(path_type&1))
 						strcat(path, ".so");
 
 					if (stat(path, &stat_buf) == -1) {
c6a1ba6d
 						LM_DBG("module file not found <%s>\n", path);
b27dfba5
 						pkg_free(path);
 						path=0;
 					}
 				}
 			} else {
 				/* try mod_path - S compat */
 				if(path==mod_path) {
 					if (stat(path, &stat_buf) == -1) {
c6a1ba6d
 						LM_DBG("module file not found <%s>\n", path);
b27dfba5
 						path=0;
 					}
 				}
 				if(path==0) {
 					/* try path <MODS_DIR>/mod_path - K compat */
 					path = (char*)pkg_malloc(mdir_len + 1 /* "/" */ +
 									strlen(mod_path) + 1);
 					if (path==0) goto error;
 					memcpy(path, mdir, mdir_len);
 					len = mdir_len;
 					if (len != 0 && path[len - 1] != '/'){
 						path[len]='/';
 						len++;
 					}
 					path[len]=0;
 					strcat(path, mod_path);
 
 					if (stat(path, &stat_buf) == -1) {
c6a1ba6d
 						LM_DBG("module file not found <%s>\n", path);
b27dfba5
 						pkg_free(path);
 						path=0;
 					}
fdbb341c
 				}
f4194f69
 			}
fdbb341c
 			mdir=nxt_mdir?nxt_mdir+1:0;
 		}while(path==0 && mdir);
 		if (path==0){
153ab772
 			LM_ERR("could not find module <%.*s> in <%s>\n",
 						modname.len, modname.s, mods_dir);
fdbb341c
 			goto error;
f4194f69
 		}
 	}
c6a1ba6d
 	LM_DBG("trying to load <%s>\n", path);
b27dfba5
 
ea31876f
 	retries=2;
 	dlflags=RTLD_NOW;
 reload:
f4d2f91c
 	handle=dlopen(path, dlflags); /* resolve all symbols now */
3bf76e49
 	if (handle==0){
153ab772
 		LM_ERR("could not open module <%s>: %s\n", path, dlerror());
3bf76e49
 		goto error;
 	}
fb851d7e
 
3bf76e49
 	for(t=modules;t; t=t->next){
 		if (t->handle==handle){
153ab772
 			LM_WARN("attempting to load the same module twice (%s)\n", path);
3bf76e49
 			goto skip;
 		}
 	}
2dcb8b67
 	/* version control */
bae7e884
 	if (!version_control(handle, path)) {
c4f7356f
 		exit(-1);
bae7e884
 	}
2873d384
 	mod_if_ver = (unsigned *)dlsym(handle,
 									DLSYM_PREFIX "module_interface_ver");
 	if ( (error =(char*)dlerror())!=0 ){
153ab772
 		LM_ERR("no module interface version in module <%s>\n", path );
2873d384
 		goto error1;
 	}
3bf76e49
 	/* launch register */
ea31876f
 	mr = (mod_register_function)dlsym(handle, DLSYM_PREFIX "mod_register");
 	if (((error =(char*)dlerror())==0) && mr) {
 		/* no error call it */
 		new_dlflags=dlflags;
931bd29d
 		if (mr(path, &new_dlflags, 0, 0)!=0) {
153ab772
 			LM_ERR("%s: mod_register failed\n", path);
ea31876f
 			goto error1;
 		}
 		if (new_dlflags!=dlflags && new_dlflags!=0) {
 			/* we have to reload the module */
 			dlclose(handle);
 			dlflags=new_dlflags;
 			retries--;
 			if (retries>0) goto reload;
153ab772
 			LM_ERR("%s: cannot agree on the dlflags\n", path);
ea31876f
 			goto error;
 		}
 	}
2873d384
 	exp = (union module_exports_u*)dlsym(handle, DLSYM_PREFIX "exports");
381659ac
 	if ( (error =(char*)dlerror())!=0 ){
153ab772
 		LM_ERR("%s\n", error);
3bf76e49
 		goto error1;
 	}
ea31876f
 	/* hack to allow for kamailio style dlflags inside exports */
 	if (*mod_if_ver == 1) {
 		new_dlflags = exp->v1.dlflags;
 		if (new_dlflags!=dlflags && new_dlflags!=DEFAULT_DLFLAGS) {
 			/* we have to reload the module */
 			dlclose(handle);
a7876417
 			DEBUG("%s: exports dlflags interface is deprecated and it will not"
d70215e5
 					" be supported in newer versions; consider using"
207f7c6f
 					" mod_register() instead\n", path);
ea31876f
 			dlflags=new_dlflags;
 			retries--;
 			if (retries>0) goto reload;
153ab772
 			LM_ERR("%s: cannot agree on the dlflags\n", path);
ea31876f
 			goto error;
 		}
 	}
2873d384
 	if (register_module(*mod_if_ver, exp, path, handle)<0) goto error1;
3bf76e49
 	return 0;
 
 error1:
 	dlclose(handle);
 error:
 skip:
fdbb341c
 	if (path && path!=mod_path)
 		pkg_free(path);
3bf76e49
 	return -1;
 }
 
 
 
2873d384
 /* searches the module list for function name in module mod and returns 
  *  a pointer to the "name" function record union or 0 if not found
dee21fff
  * sets also *mod_if_ver to the original module interface version.
2873d384
  * mod==0 is a wildcard matching all modules
49b28ea4
  * flags parameter is OR value of all flags that must match
51c38611
  */
dee21fff
 sr31_cmd_export_t* find_mod_export_record(char* mod, char* name,
2873d384
 											int param_no, int flags,
 											unsigned* mod_if_ver)
3bf76e49
 {
 	struct sr_module* t;
dee21fff
 	sr31_cmd_export_t* cmd;
3bf76e49
 
 	for(t=modules;t;t=t->next){
dee21fff
 		if (mod!=0 && (strcmp(t->exports.name, mod) !=0))
2873d384
 			continue;
dee21fff
 		if (t->exports.cmds)
 			for(cmd=&t->exports.cmds[0]; cmd->name; cmd++) {
 				if((strcmp(name, cmd->name) == 0) &&
 					((cmd->param_no == param_no) ||
 					 (cmd->param_no==VAR_PARAM_NO)) &&
 					((cmd->flags & flags) == flags)
 				){
c6a1ba6d
 					LM_DBG("find_export_record: found <%s> in module %s [%s]\n",
dee21fff
 						name, t->exports.name, t->path);
 					*mod_if_ver=t->orig_mod_interface_ver;
 					return cmd;
 				}
 			}
3bf76e49
 	}
c6a1ba6d
 	LM_DBG("find_export_record: <%s> not found \n", name);
3bf76e49
 	return 0;
 }
 
34fd2612
 
2873d384
 
 /* searches the module list for function name and returns 
  *  a pointer to the "name" function record union or 0 if not found
  * sets also *mod_if_ver to the module interface version (needed to know
  * which member of the union should be accessed v0 or v1)
  * mod==0 is a wildcard matching all modules
  * flags parameter is OR value of all flags that must match
  */
dee21fff
 sr31_cmd_export_t* find_export_record(char* name,
2873d384
 											int param_no, int flags,
 											unsigned* mod_if_ver)
 {
 	return find_mod_export_record(0, name, param_no, flags, mod_if_ver);
 }
 
 
 
f141bc93
 cmd_function find_export(char* name, int param_no, int flags)
 {
dee21fff
 	sr31_cmd_export_t* cmd;
2873d384
 	unsigned mver;
 	
 	cmd = find_export_record(name, param_no, flags, &mver);
dee21fff
 	return cmd?cmd->function:0;
f141bc93
 }
 
 
300b0f50
 rpc_export_t* find_rpc_export(char* name, int flags)
 {
5099fd64
 	return rpc_lookup((char*)name, strlen(name));
300b0f50
 }
 
49b28ea4
 
 /*
2873d384
  * searches the module list and returns pointer to "name" function in module
  * "mod"
49b28ea4
  * 0 if not found
  * flags parameter is OR value of all flags that must match
  */
 cmd_function find_mod_export(char* mod, char* name, int param_no, int flags)
 {
dee21fff
 	sr31_cmd_export_t* cmd;
2873d384
 	unsigned mver;
49b28ea4
 
2873d384
 	cmd=find_mod_export_record(mod, name, param_no, flags, &mver);
 	if (cmd)
dee21fff
 		return cmd->function;
2873d384
 	
c6a1ba6d
 	LM_DBG("<%s> in module <%s> not found\n", name, mod);
49b28ea4
 	return 0;
 }
 
 
fb851d7e
 struct sr_module* find_module_by_name(char* mod) {
 	struct sr_module* t;
 
 	for(t = modules; t; t = t->next) {
dee21fff
 		if (strcmp(mod, t->exports.name) == 0) {
fb851d7e
 			return t;
 		}
 	}
c6a1ba6d
 	LM_DBG("module <%s> not found\n", mod);
fb851d7e
 	return 0;
 }
49b28ea4
 
 
a7524985
 /*!
  * \brief Find a parameter with given type
  * \param mod module
  * \param name parameter name
  * \param type_mask parameter mask
  * \param param_type parameter type
  * \return parameter address in memory, if there is no such parameter, NULL is returned
  */
2873d384
 void* find_param_export(struct sr_module* mod, char* name,
 						modparam_t type_mask, modparam_t *param_type)
31309a3a
 {
6419a43f
 	param_export_t* param;
31309a3a
 
fb851d7e
 	if (!mod)
 		return 0;
dee21fff
 	for(param = mod->exports.params ;param && param->name ; param++) {
fb851d7e
 		if ((strcmp(name, param->name) == 0) &&
 			((param->type & PARAM_TYPE_MASK(type_mask)) != 0)) {
c6a1ba6d
 			LM_DBG("found <%s> in module %s [%s]\n",
dee21fff
 				name, mod->exports.name, mod->path);
fb851d7e
 			*param_type = param->type;
 			return param->param_pointer;
31309a3a
 		}
 	}
c6a1ba6d
 	LM_DBG("parameter <%s> not found in module <%s>\n",
dee21fff
 			name, mod->exports.name);
31309a3a
 	return 0;
 }
 
 
bf08223a
 void destroy_modules()
 {
3c8bd369
 	struct sr_module* t, *foo;
 
2396dde7
 	/* call first destroy function from each module */
3c8bd369
 	t=modules;
 	while(t) {
 		foo=t->next;
dee21fff
 		if (t->exports.destroy_f){
 			t->exports.destroy_f();
2873d384
 		}
2396dde7
 		t=foo;
 	}
 	/* free module exports structures */
 	t=modules;
 	while(t) {
 		foo=t->next;
3c8bd369
 		pkg_free(t);
 		t=foo;
 	}
8aeb47e2
 	modules=0;
55faef7d
 	if (mod_response_cbks){
 		pkg_free(mod_response_cbks);
 		mod_response_cbks=0;
 	}
bf08223a
 }
31309a3a
 
9243edb9
 #ifdef NO_REVERSE_INIT
31309a3a
 
 /*
  * Initialize all loaded modules, the initialization
  * is done *AFTER* the configuration file is parsed
  */
 int init_modules(void)
 {
 	struct sr_module* t;
fb851d7e
 
d5a22ca8
 	if(async_task_init()<0)
 		return -1;
 
31309a3a
 	for(t = modules; t; t = t->next) {
52d33940
 		if (t->exports.init_f) {
dee21fff
 			if (t->exports.init_f() != 0) {
153ab772
 				LM_ERR("Error while initializing module %s\n", t->exports.name);
dee21fff
 				return -1;
31309a3a
 			}
52d33940
 			/* delay next module init, if configured */
 			if(unlikely(modinit_delay>0))
 				sleep_us(modinit_delay);
 		}
dee21fff
 		if (t->exports.response_f)
 			mod_response_cbk_no++;
55faef7d
 	}
 	mod_response_cbks=pkg_malloc(mod_response_cbk_no * 
 									sizeof(response_function));
 	if (mod_response_cbks==0){
153ab772
 		LM_ERR("memory allocation failure for %d response_f callbacks\n",
 					mod_response_cbk_no);
55faef7d
 		return -1;
 	}
dee21fff
 	for (t=modules, i=0; t && (i<mod_response_cbk_no); t=t->next) {
 		if (t->exports.response_f) {
 			mod_response_cbks[i]=t->exports.response_f;
 			i++;
55faef7d
 		}
31309a3a
 	}
 	return 0;
 }
9243edb9
 
dee21fff
 
 
9243edb9
 /*
  * per-child initialization
  */
 int init_child(int rank)
 {
 	struct sr_module* t;
cb87691a
 	char* type;
 
 	switch(rank) {
 	case PROC_MAIN:     type = "PROC_MAIN";     break;
 	case PROC_TIMER:    type = "PROC_TIMER";    break;
 	case PROC_FIFO:     type = "PROC_FIFO";     break;
 	case PROC_TCP_MAIN: type = "PROC_TCP_MAIN"; break;
 	default:            type = "CHILD";         break;
 	}
c6a1ba6d
 	LM_DBG("initializing %s with rank %d\n", type, rank);
fb851d7e
 
d5a22ca8
 	if(async_task_child_init(rank)<0)
 		return -1;
9243edb9
 
 	for(t = modules; t; t = t->next) {
dee21fff
 		if (t->exports.init_child_f) {
 			if ((t->exports.init_child_f(rank)) < 0) {
153ab772
 				LM_ERR("Initialization of child %d failed\n", rank);
9243edb9
 				return -1;
dee21fff
 			}
9243edb9
 		}
 	}
 	return 0;
 }
 
 #else
 
 
 /* recursive module child initialization; (recursion is used to
    process the module linear list in the same order in
    which modules are loaded in config file
 */
 
 static int init_mod_child( struct sr_module* m, int rank )
 {
 	if (m) {
 		/* iterate through the list; if error occurs,
 		   propagate it up the stack
 		 */
 		if (init_mod_child(m->next, rank)!=0) return -1;
dee21fff
 		if (m->exports.init_child_f) {
c6a1ba6d
 			LM_DBG("rank %d: %s\n", rank, m->exports.name);
dee21fff
 			if (m->exports.init_child_f(rank)<0) {
153ab772
 				LM_ERR("Error while initializing module %s (%s)\n",
dee21fff
 							m->exports.name, m->path);
 				return -1;
 			} else {
 				/* module correctly initialized */
 				return 0;
9243edb9
 			}
 		}
dee21fff
 		/* no init function -- proceed with success */
9243edb9
 		return 0;
 	} else {
 		/* end of list */
 		return 0;
 	}
 }
 
 
 /*
  * per-child initialization
  */
 int init_child(int rank)
 {
d5a22ca8
 	if(async_task_child_init(rank)<0)
 		return -1;
 
9243edb9
 	return init_mod_child(modules, rank);
 }
 
 
 
 /* recursive module initialization; (recursion is used to
    process the module linear list in the same order in
    which modules are loaded in config file
 */
 
 static int init_mod( struct sr_module* m )
 {
 	if (m) {
 		/* iterate through the list; if error occurs,
 		   propagate it up the stack
 		 */
 		if (init_mod(m->next)!=0) return -1;
dee21fff
 			if (m->exports.init_f) {
c6a1ba6d
 				LM_DBG("%s\n", m->exports.name);
dee21fff
 				if (m->exports.init_f()!=0) {
153ab772
 					LM_ERR("Error while initializing module %s (%s)\n",
dee21fff
 								m->exports.name, m->path);
 					return -1;
 				} else {
 					/* module correctly initialized */
2873d384
 					return 0;
dee21fff
 				}
9243edb9
 			}
dee21fff
 			/* no init function -- proceed with success */
 			return 0;
9243edb9
 	} else {
 		/* end of list */
 		return 0;
 	}
 }
 
 /*
  * Initialize all loaded modules, the initialization
  * is done *AFTER* the configuration file is parsed
  */
 int init_modules(void)
 {
55faef7d
 	struct sr_module* t;
 	int i;
 	
d5a22ca8
 	if(async_task_init()<0)
 		return -1;
 
e958eaf6
 	i = init_mod(modules);
 	if(i!=0)
 		return i;
 
55faef7d
 	for(t = modules; t; t = t->next)
dee21fff
 		if (t->exports.response_f)
 			mod_response_cbk_no++;
55faef7d
 	mod_response_cbks=pkg_malloc(mod_response_cbk_no * 
 									sizeof(response_function));
 	if (mod_response_cbks==0){
153ab772
 		LM_ERR("memory allocation failure for %d response_f callbacks\n", mod_response_cbk_no);
55faef7d
 		return -1;
 	}
dee21fff
 	for (t=modules, i=0; t && (i<mod_response_cbk_no); t=t->next)
 		if (t->exports.response_f) {
 			mod_response_cbks[i]=t->exports.response_f;
 			i++;
55faef7d
 		}
 	
e958eaf6
 	return 0;
9243edb9
 }
 
 #endif
bb6575cd
 
 
f2f12966
 action_u_t *fixup_get_param(void **cur_param, int cur_param_no,
 							int required_param_no)
 {
b2383262
 	action_u_t *a;
f4194f69
 	/* cur_param points to a->u.string, get pointer to a */
b2383262
 	a = (void*) ((char *)cur_param - offsetof(action_u_t, u.string));
f141bc93
 	return a + required_param_no - cur_param_no;
 }
 
f2f12966
 int fixup_get_param_count(void **cur_param, int cur_param_no)
 {
f141bc93
 	action_u_t *a;
 	a = fixup_get_param(cur_param, cur_param_no, 0);
 	if (a)
 		return a->u.number;
 	else
 		return -1;
 }
6d35d70e
 
 
35657139
 
 /** get a pointer to a parameter internal type.
  * @param param
  * @return pointer to the parameter internal type.
  */
 action_param_type* fixup_get_param_ptype(void** param)
 {
 	action_u_t* a;
b2383262
 	a = (void*)((char*)param - offsetof(action_u_t, u.string));
35657139
 	return &a->type;
 }
 
 
 /** get a parameter internal type.
  * @see fixup_get_param_ptype().
  * @return paramter internal type.
  */
 action_param_type fixup_get_param_type(void** param)
 {
 	return *fixup_get_param_ptype(param);
 }
 
 
 
6d35d70e
 /* fixes flag params (resolves possible named flags)
  * use PARAM_USE_FUNC|PARAM_STRING as a param. type and create
  * a wrapper function that does just:
  * return fix_flag(type, val, "my_module", "my_param", &flag_var)
  * see also param_func_t.
  */
 int fix_flag( modparam_t type, void* val,
 					char* mod_name, char* param_name, int* flag)
 {
 	int num;
 	int err;
 	int f, len;
 	char* s;
 	char *p;
f4194f69
 
6d35d70e
 	if ((type & PARAM_STRING)==0){
153ab772
 		LM_CRIT("%s: fix_flag(%s): bad parameter type\n",
6d35d70e
 					mod_name, param_name);
 		return -1;
 	}
 	s=(char*)val;
 	len=strlen(s);
 	f=-1;
 	/* try to see if it's a number */
 	num = str2s(s, len, &err);
 	if (err != 0) {
 		/* see if it's in the name:<no> format */
 		p=strchr(s, ':');
 		if (p){
 			f= str2s(p+1, strlen(p+1), &err);
 			if (err!=0){
153ab772
 				LM_ERR("%s: invalid %s format: \"%s\"",
 						mod_name, param_name, s);
6d35d70e
 				return -1;
 			}
 			*p=0;
 		}
 		if ((num=get_flag_no(s, len))<0){
 			/* not declared yet, declare it */
 			num=register_flag(s, f);
 		}
 		if (num<0){
153ab772
 			LM_ERR("%s: bad %s %s\n", mod_name, param_name, s);
6d35d70e
 			return -1;
 		} else if ((f>0) && (num!=f)){
153ab772
 			LM_ERR("%s: flag %s already defined"
6d35d70e
 					" as %d (and not %d), using %s:%d\n",
 					mod_name, s, num, f, s, num);
 		}
 	}
 	*flag=num;
 	return 0;
 }
b91364c6
 
d0ce0ebd
 /*
  * Common function parameter fixups
  */
b91364c6
 
f2f12966
 /** Generic parameter fixup function.
  *  Creates a fparam_t structure.
  *  @param type  contains allowed parameter types
  *  @param param is the parameter that will be fixed-up
cd892e69
  *
f2f12966
  * @return
cd892e69
  *    0 on success, 
  *    1 if the param doesn't match the specified type
  *    <0 on failure
b91364c6
  */
 int fix_param(int type, void** param)
f4194f69
 {
f2f12966
 	fparam_t* p;
 	str name, s;
67436c8f
 	int num;
f2f12966
 	int err;
f4194f69
 
f2f12966
 	p = (fparam_t*)pkg_malloc(sizeof(fparam_t));
 	if (!p) {
153ab772
 		LM_ERR("No memory left\n");
f2f12966
 		return E_OUT_OF_MEM;
d0ce0ebd
 	}
f2f12966
 	memset(p, 0, sizeof(fparam_t));
 	p->orig = *param;
 	
 	switch(type) {
 		case FPARAM_UNSPEC:
153ab772
 			LM_ERR("Invalid type value\n");
f2f12966
 			goto error;
 		case FPARAM_STRING:
 			p->v.asciiz = *param;
b25e31aa
 			/* no break */
f2f12966
 		case FPARAM_STR:
 			p->v.str.s = (char*)*param;
 			p->v.str.len = strlen(p->v.str.s);
235a9c07
 			p->fixed = &p->v;
f2f12966
 			break;
 		case FPARAM_INT:
 			s.s = (char*)*param;
 			s.len = strlen(s.s);
67436c8f
 			err = str2sint(&s, &num);
f2f12966
 			if (err == 0) {
 				p->v.i = (int)num;
 			} else {
 				/* Not a number */
 				pkg_free(p);
 				return 1;
 			}
235a9c07
 			p->fixed = (void*)(long)num;
f2f12966
 			break;
 		case FPARAM_REGEX:
 			if ((p->v.regex = pkg_malloc(sizeof(regex_t))) == 0) {
153ab772
 				LM_ERR("No memory left\n");
235a9c07
 				goto error;
f2f12966
 			}
 			if (regcomp(p->v.regex, *param,
 						REG_EXTENDED|REG_ICASE|REG_NEWLINE)) {
 				pkg_free(p->v.regex);
ad4f08c9
 				p->v.regex=0;
b25e31aa
 				/* not a valid regex */
 				goto no_match;
f2f12966
 			}
15d47d5c
 			p->fixed = p->v.regex;
f2f12966
 			break;
 		case FPARAM_AVP:
 			name.s = (char*)*param;
 			name.len = strlen(name.s);
 			trim(&name);
 			if (!name.len || name.s[0] != '$') {
 				/* Not an AVP identifier */
b25e31aa
 				goto no_match;
f2f12966
 			}
 			name.s++;
 			name.len--;
 			if (parse_avp_ident(&name, &p->v.avp) < 0) {
b25e31aa
 				/* invalid avp identifier (=> no match) */
 				goto no_match;
f2f12966
 			}
235a9c07
 			p->fixed = &p->v;
f2f12966
 			break;
 		case FPARAM_SELECT:
 			name.s = (char*)*param;
 			name.len = strlen(name.s);
 			trim(&name);
 			if (!name.len || name.s[0] != '@') {
 				/* Not a select identifier */
b25e31aa
 				goto no_match;
f2f12966
 			}
 			if (parse_select(&name.s, &p->v.select) < 0) {
153ab772
 				LM_ERR("Error while parsing select identifier\n");
f2f12966
 				goto error;
 			}
235a9c07
 			p->fixed = &p->v;
f2f12966
 			break;
 		case FPARAM_SUBST:
 			s.s = *param;
 			s.len = strlen(s.s);
 			p->v.subst = subst_parser(&s);
 			if (!p->v.subst) {
153ab772
 				LM_ERR("Error while parsing regex substitution\n");
ad4f08c9
 				goto error;
 			}
235a9c07
 			p->fixed = &p->v;
ad4f08c9
 			break;
 		case FPARAM_PVS:
 			name.s = (char*)*param;
 			name.len = strlen(name.s);
 			trim(&name);
 			if (!name.len || name.s[0] != '$'){
 				/* not a pvs identifier */
b25e31aa
 				goto no_match;
ad4f08c9
 			}
 			p->v.pvs=pkg_malloc(sizeof(pv_spec_t));
 			if (p->v.pvs==0){
153ab772
 				LM_ERR("out of memory while parsing pv_spec_t\n");
ad4f08c9
 				goto error;
 			}
b25e31aa
 			if (pv_parse_spec2(&name, p->v.pvs, 1)==0){
 				/* not a valid pvs identifier (but it might be an avp) */
ad4f08c9
 				pkg_free(p->v.pvs);
 				p->v.pvs=0;
b25e31aa
 				goto no_match;
ad4f08c9
 			}
d64c1c71
 			p->fixed = p->v.pvs;
ad4f08c9
 			break;
 		case FPARAM_PVE:
 			name.s = (char*)*param;
 			name.len = strlen(name.s);
 			if (pv_parse_format(&name, &p->v.pve)<0){
153ab772
 				LM_ERR("bad PVE format: \"%.*s\"\n", name.len, name.s);
ad4f08c9
 				goto error;
f2f12966
 			}
235a9c07
 			p->fixed = &p->v;
f2f12966
 			break;
b91364c6
 	}
f2f12966
 	
 	p->type = type;
 	*param = (void*)p;
 	return 0;
 	
b25e31aa
 no_match:
 	pkg_free(p);
 	return 1;
f2f12966
 error:
 	pkg_free(p);
 	return E_UNSPEC;
d0ce0ebd
 }
b91364c6
 
 
ad4f08c9
 
 /** fparam_t free function.
  *  Frees the "content" of a fparam, but not the fparam itself.
6a40b4bd
  *  Note: it doesn't free fp->orig!
ad4f08c9
  *  Assumes pkg_malloc'ed content.
  *  @param fp -  fparam to be freed
  *
  */
 void fparam_free_contents(fparam_t* fp)
 {
 
 	if (fp==0)
 		return;
 	switch(fp->type) {
 		case FPARAM_UNSPEC:
 		case FPARAM_STRING: /* asciiz string, not str */
 		case FPARAM_INT:
 		case FPARAM_STR:
 			/* nothing to do */
af8f3e15
 			break;
ad4f08c9
 		case FPARAM_REGEX:
 			if (fp->v.regex){
 				regfree(fp->v.regex);
 				pkg_free(fp->v.regex);
 				fp->v.regex=0;
 			}
 			break;
 		case FPARAM_AVP:
 			free_avp_name(&fp->v.avp.flags, &fp->v.avp.name);
 			break;
 		case FPARAM_SELECT:
 			if (fp->v.select){
 				free_select(fp->v.select);
 				fp->v.select=0;
 			}
 			break;
 		case FPARAM_SUBST:
 			if (fp->v.subst){
 				subst_expr_free(fp->v.subst);
 				fp->v.subst=0;
 			}
 			break;
 		case FPARAM_PVS:
 			if (fp->v.pvs){
 				pv_spec_free(fp->v.pvs);
 				fp->v.pvs=0;
 			}
 			break;
 		case FPARAM_PVE:
 			if (fp->v.pve){
 				pv_elem_free_all(fp->v.pve);
 				fp->v.pve=0;
 			}
 			break;
 	}
6a40b4bd
 }
 
 
a7524985
 /**
  * @brief Generic free fixup type function for a fixed fparam
  * 
  * Generic free fixup type function for a fixed fparam. It will free whatever
  * was allocated during the initial fparam fixup and restore the original param
  * value.
  * @param param freed parameters
6a40b4bd
  */
 void fparam_free_restore(void** param)
 {
 	fparam_t *fp;
 	void *orig;
 	
 	fp = *param;
 	orig = fp->orig;
 	fp->orig = 0;
 	fparam_free_contents(fp);
 	pkg_free(fp);
 	*param = orig;
ad4f08c9
 }
 
 
bf9d1c43
 
 /** fix a param to one of the given types (mask).
   *
   * @param types - bitmap of the allowed types (e.g. FPARAM_INT|FPARAM_STR)
   * @param param - value/result
   * @return - 0 on success, -1 on error, 1 if param doesn't
   *           match any of the types
   */
 int fix_param_types(int types, void** param)
 {
 	int ret;
 	int t;
 	
35657139
 	if (fixup_get_param_type(param) == STRING_RVE_ST &&
 			(types & (FPARAM_INT|FPARAM_STR|FPARAM_STRING))) {
 		/* if called with a RVE already converted to string =>
 		   don't try AVP, PVAR or SELECT (to avoid double
 		   deref., e.g.: $foo="$bar"; f($foo) ) */
 		types &= ~ (FPARAM_AVP|FPARAM_PVS|FPARAM_SELECT|FPARAM_PVE);
 	}
bf9d1c43
 	for (t=types & ~(types-1); types; types&=(types-1), t=types & ~(types-1)){
 		if ((ret=fix_param(t, param))<=0) return ret;
 	}
 	return E_UNSPEC;
 }
 
 
 
d0ce0ebd
 /*
  * Fixup variable string, the parameter can be
  * AVP, SELECT, or ordinary string. AVP and select
  * identifiers will be resolved to their values during
  * runtime
  *
  * The parameter value will be converted to fparam structure
  * This function returns -1 on an error
  */
 int fixup_var_str_12(void** param, int param_no)
 {
f2f12966
 	int ret;
35657139
 	if (fixup_get_param_type(param) != STRING_RVE_ST) {
 		/* if called with a RVE already converted to string =>
 		   don't try AVP, PVAR or SELECT (to avoid double
 		   deref., e.g.: $foo="$bar"; f($foo) ) */
99cff510
 		if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
35657139
 		if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
 		if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
 	}
f2f12966
 	if ((ret = fix_param(FPARAM_STR, param)) <= 0) return ret;
153ab772
 	LM_ERR("Error while fixing parameter, PV, AVP, SELECT, and str conversions"
f2f12966
 			" failed\n");
 	return -1;
d0ce0ebd
 }
b91364c6
 
d0ce0ebd
 /* Same as fixup_var_str_12 but applies to the 1st parameter only */
 int fixup_var_str_1(void** param, int param_no)
 {
f2f12966
 	if (param_no == 1) return fixup_var_str_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
c16e9887
 
d0ce0ebd
 /* Same as fixup_var_str_12 but applies to the 2nd parameter only */
 int fixup_var_str_2(void** param, int param_no)
 {
f2f12966
 	if (param_no == 2) return fixup_var_str_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
b91364c6
 
4c52cf05
 /** fixup variable-pve-only-string.
  * The parameter can be a PVE (pv based format string)
  * or string.
  * non-static PVEs  identifiers will be resolved to
  * their values during runtime.
  * The parameter value will be converted to fparam structure
  * @param  param - double pointer to param, as for normal fixup functions.
  * @param  param_no - parameter number, ignored.
  * @return -1 on an error, 0 on success.
  */
 int fixup_var_pve_12(void** param, int param_no)
 {
 	int ret;
 	fparam_t* fp;
 	if (fixup_get_param_type(param) != STRING_RVE_ST) {
 		/* if called with a RVE already converted to string =>
 		   don't try PVE again (to avoid double
 		   deref., e.g.: $foo="$bar"; f($foo) ) */
 		if ((ret = fix_param(FPARAM_PVE, param)) <= 0) {
 			if (ret < 0)
 				return ret;
 			/* check if it resolved to a dynamic or "static" PVE.
 			   If the resulting PVE is static (normal string), discard
 			   it and use the normal string fixup (faster at runtime) */
 			fp = (fparam_t*)*param;
2895dbdc
 			if (fp->v.pve->spec == 0 || fp->v.pve->spec->getf == 0)
4c52cf05
 				fparam_free_restore(param); /* fallback to STR below */
 			else
 				return ret; /* dynamic PVE => return */
 		}
 		
 	}
 	if ((ret = fix_param(FPARAM_STR, param)) <= 0) return ret;
153ab772
 	LM_ERR("Error while fixing parameter - PVE or str conversions failed\n");
4c52cf05
 	return -1;
 }
 
b91364c6
 
a5b499a4
 
 /** fixup variable-pve-string.
  * The parameter can be a PVAR, AVP, SELECT, PVE (pv based format string)
  * or string.
  * PVAR, AVP and select and non-static PVEs  identifiers will be resolved to
  * their values during runtime.
  * The parameter value will be converted to fparam structure
  * @param  param - double pointer to param, as for normal fixup functions.
  * @param  param_no - parameter number, ignored.
  * @return -1 on an error, 0 on success.
  */
 int fixup_var_pve_str_12(void** param, int param_no)
 {
 	int ret;
 	fparam_t* fp;
 	if (fixup_get_param_type(param) != STRING_RVE_ST) {
 		/* if called with a RVE already converted to string =>
 		   don't try AVP, PVAR, SELECT or PVE again (to avoid double
 		   deref., e.g.: $foo="$bar"; f($foo) ) */
 		if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
 		if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
 		if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
 		if ((ret = fix_param(FPARAM_PVE, param)) <= 0) {
 			if (ret < 0)
 				return ret;
 			/* check if it resolved to a dynamic or "static" PVE.
 			   If the resulting PVE is static (normal string), discard
 			   it and use the normal string fixup (faster at runtime) */
 			fp = (fparam_t*)*param;
2895dbdc
 			if (fp->v.pve->spec == 0 || fp->v.pve->spec->getf == 0)
a5b499a4
 				fparam_free_restore(param); /* fallback to STR below */
 			else
 				return ret; /* dynamic PVE => return */
 		}
 		
 	}
 	if ((ret = fix_param(FPARAM_STR, param)) <= 0) return ret;
153ab772
 	LM_ERR("Error while fixing parameter, PV, AVP, SELECT, and str conversions"
a5b499a4
 			" failed\n");
 	return -1;
 }
 
 /* Same as fixup_var_pve_str_12 but applies to the 1st parameter only */
 int fixup_var_pve_str_1(void** param, int param_no)
 {
 	if (param_no == 1) return fixup_var_pve_str_12(param, param_no);
 	else return 0;
 }
 
 /* Same as fixup_var_pve_str_12 but applies to the 2nd parameter only */
 int fixup_var_pve_str_2(void** param, int param_no)
 {
 	if (param_no == 2) return fixup_var_pve_str_12(param, param_no);
 	else return 0;
 }
 
 
 
d0ce0ebd
 /*
  * Fixup variable integer, the parameter can be
  * AVP, SELECT, or ordinary integer. AVP and select
f4194f69
  * identifiers will be resolved to their values and
d0ce0ebd
  * converted to int if necessary during runtime
  *
  * The parameter value will be converted to fparam structure
  * This function returns -1 on an error
  */
 int fixup_var_int_12(void** param, int param_no)
 {
f2f12966
 	int ret;
35657139
 	if (fixup_get_param_type(param) != STRING_RVE_ST) {
 		/* if called with a RVE already converted to string =>
 		   don't try AVP, PVAR or SELECT (to avoid double
 		   deref., e.g.: $foo="$bar"; f($foo) ) */
99cff510
 		if ((ret = fix_param(FPARAM_PVS, param)) <= 0) return ret;
35657139
 		if ((ret = fix_param(FPARAM_AVP, param)) <= 0) return ret;
 		if ((ret = fix_param(FPARAM_SELECT, param)) <= 0) return ret;
 	}
f2f12966
 	if ((ret = fix_param(FPARAM_INT, param)) <= 0) return ret;
153ab772
 	LM_ERR("Error while fixing parameter, PV, AVP, SELECT, and int conversions"
f2f12966
 			" failed\n");
 	return -1;
d0ce0ebd
 }
 
 /* Same as fixup_var_int_12 but applies to the 1st parameter only */
 int fixup_var_int_1(void** param, int param_no)
 {
f2f12966
 	if (param_no == 1) return fixup_var_int_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 /* Same as fixup_var_int_12 but applies to the 2nd parameter only */
 int fixup_var_int_2(void** param, int param_no)
 {
f2f12966
 	if (param_no == 2) return fixup_var_int_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 
 /*
  * The parameter must be a regular expression which must compile, the
  * parameter will be converted to compiled regex
  */
 int fixup_regex_12(void** param, int param_no)
 {
f2f12966
 	int ret;
d0ce0ebd
 
f2f12966
 	if ((ret = fix_param(FPARAM_REGEX, param)) <= 0) return ret;
153ab772
 	LM_ERR("Error while compiling regex in function parameter\n");
f2f12966
 	return -1;
d0ce0ebd
 }
 
 /* Same as fixup_regex_12 but applies to the 1st parameter only */
 int fixup_regex_1(void** param, int param_no)
 {
f2f12966
 	if (param_no == 1) return fixup_regex_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 /* Same as fixup_regex_12 but applies to the 2nd parameter only */
 int fixup_regex_2(void** param, int param_no)
 {
f2f12966
 	if (param_no == 2) return fixup_regex_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 /*
  * The string parameter will be converted to integer
  */
 int fixup_int_12(void** param, int param_no)
 {
f2f12966
 	int ret;
d0ce0ebd
 
f2f12966
 	if ((ret = fix_param(FPARAM_INT, param)) <= 0) return ret;
153ab772
 	LM_ERR("Cannot function parameter to integer\n");
f2f12966
 	return -1;
d0ce0ebd
 
 }
 
 /* Same as fixup_int_12 but applies to the 1st parameter only */
 int fixup_int_1(void** param, int param_no)
 {
f2f12966
 	if (param_no == 1) return fixup_int_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 /* Same as fixup_int_12 but applies to the 2nd parameter only */
 int fixup_int_2(void** param, int param_no)
 {
f2f12966
 	if (param_no == 2) return fixup_int_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 /*
  * Parse the parameter as static string, do not resolve
  * AVPs or selects, convert the parameter to str structure
  */
 int fixup_str_12(void** param, int param_no)
 {
f2f12966
 	int ret;
d0ce0ebd
 
f2f12966
 	if ((ret = fix_param(FPARAM_STR, param)) <= 0) return ret;
153ab772
 	LM_ERR("Cannot function parameter to string\n");
f2f12966
 	return -1;
d0ce0ebd
 }
 
 /* Same as fixup_str_12 but applies to the 1st parameter only */
 int fixup_str_1(void** param, int param_no)
 {
f2f12966
 	if (param_no == 1) return fixup_str_12(param, param_no);
 	else return 0;
d0ce0ebd
 }
 
 /* Same as fixup_str_12 but applies to the 2nd parameter only */
 int fixup_str_2(void** param, int param_no)
 {
f2f12966
 	if (param_no == 2) return fixup_str_12(param, param_no);
 	else return 0;
b91364c6
 }
c16e9887
 
 
8f281796
 
39af8f24
 /** Get the function parameter value as string.
  *  @return  0 - Success
  *          -1 - Cannot get value
c16e9887
  */
 int get_str_fparam(str* dst, struct sip_msg* msg, fparam_t* param)
 {
f2f12966
 	int_str val;
 	int ret;
 	avp_t* avp;
39af8f24
 	pv_value_t pv_val;
f2f12966
 	
 	switch(param->type) {
 		case FPARAM_REGEX:
 		case FPARAM_UNSPEC:
 		case FPARAM_INT:
 			return -1;
 		case FPARAM_STRING:
 			dst->s = param->v.asciiz;
 			dst->len = strlen(param->v.asciiz);
 			break;
 		case FPARAM_STR:
 			*dst = param->v.str;
 			break;
 		case FPARAM_AVP:
 			avp = search_first_avp(param->v.avp.flags, param->v.avp.name,
 									&val, 0);
39af8f24
 			if (unlikely(!avp)) {
c6a1ba6d
 				LM_DBG("Could not find AVP from function parameter '%s'\n",
f2f12966
 						param->orig);
 				return -1;
 			}
39af8f24
 			if (likely(avp->flags & AVP_VAL_STR)) {
f2f12966
 				*dst = val.s;
 			} else {
 		 		/* The caller does not know of what type the AVP will be so
 				 * convert int AVPs into string here
 				 */
 				dst->s = int2str(val.n, &dst->len);
 			}
 			break;
 		case FPARAM_SELECT:
 			ret = run_select(dst, param->v.select, msg);
39af8f24
 			if (unlikely(ret < 0 || ret > 0)) return -1;
 			break;
 		case FPARAM_PVS:
 			if (likely((pv_get_spec_value(msg, param->v.pvs, &pv_val)==0) &&
 					   ((pv_val.flags&(PV_VAL_NULL|PV_VAL_STR))==PV_VAL_STR))){
 					*dst=pv_val.rs;
 			}else{
153ab772
 				LM_ERR("Could not convert PV to str\n");
39af8f24
 				return -1;
 			}
 			break;
 		case FPARAM_PVE:
2f292107
 			dst->s=pv_get_buffer();
 			dst->len=pv_get_buffer_size();
8f281796
 			if (unlikely(pv_printf(msg, param->v.pve, dst->s, &dst->len)!=0)){
153ab772
 				LM_ERR("Could not convert the PV-formated string to str\n");
39af8f24
 				dst->len=0;
 				return -1;
 			};
f2f12966
 			break;
c16e9887
 	}
f2f12966
 	return 0;
c16e9887
 }
 
d0ce0ebd
 
39af8f24
 /** Get the function parameter value as integer.
  *  @return  0 - Success
  *          -1 - Cannot get value
d0ce0ebd
  */
 int get_int_fparam(int* dst, struct sip_msg* msg, fparam_t* param)
 {
f2f12966
 	int_str val;
 	int ret;
 	avp_t* avp;
 	str tmp;
39af8f24
 	pv_value_t pv_val;
f4194f69
 
f2f12966
 	switch(param->type) {
 		case FPARAM_INT:
 			*dst = param->v.i;
 			return 0;
 		case FPARAM_REGEX:
 		case FPARAM_UNSPEC:
 		case FPARAM_STRING:
 		case FPARAM_STR:
 			return -1;
 		case FPARAM_AVP:
 			avp = search_first_avp(param->v.avp.flags, param->v.avp.name,
 									&val, 0);
39af8f24
 			if (unlikely(!avp)) {
c6a1ba6d
 				LM_DBG("Could not find AVP from function parameter '%s'\n",
f2f12966
 						param->orig);
 				return -1;
 			}
 			if (avp->flags & AVP_VAL_STR) {
 				if (str2int(&val.s, (unsigned int*)dst) < 0) {
153ab772
 					LM_ERR("Could not convert AVP string value to int\n");
f2f12966
 					return -1;
 				}
 			} else {
 				*dst = val.n;
 			}
 			break;
 		case FPARAM_SELECT:
 			ret = run_select(&tmp, param->v.select, msg);
39af8f24
 			if (unlikely(ret < 0 || ret > 0)) return -1;
 			if (unlikely(str2int(&tmp, (unsigned int*)dst) < 0)) {
153ab772
 				LM_ERR("Could not convert select result to int\n");
f2f12966
 				return -1;
 			}
 			break;
39af8f24
 		case FPARAM_PVS:
 			if (likely((pv_get_spec_value(msg, param->v.pvs, &pv_val)==0) &&
 					   ((pv_val.flags&(PV_VAL_NULL|PV_VAL_INT))==PV_VAL_INT))){
 					*dst=pv_val.ri;
 			}else{
153ab772
 				LM_ERR("Could not convert PV to int\n");
39af8f24
 				return -1;
 			}
 			break;
 		case FPARAM_PVE:
 			return -1;
d0ce0ebd
 	}
f2f12966
 	return 0;
d0ce0ebd
 }
44c65404
 
8a334551
 /** Get the function parameter value as string or/and integer (if possible).
  *  @return  0 - Success
  *          -1 - Cannot get value
  */
 int get_is_fparam(int* i_dst, str* s_dst, struct sip_msg* msg, fparam_t* param, unsigned int *flags)
 {
 	int_str val;
 	int ret;
 	avp_t* avp;
 	str tmp;
 	pv_value_t pv_val;
 
 	*flags = 0;
 	switch(param->type) {
 		case FPARAM_INT:
 			*i_dst = param->v.i;
 			*flags |= PARAM_INT;
 			return 0;
 		case FPARAM_REGEX:
 		case FPARAM_UNSPEC:
 		case FPARAM_STRING:
 			s_dst->s = param->v.asciiz;
 			s_dst->len = strlen(param->v.asciiz);
 			*flags |= PARAM_STR;
 			break;
 		case FPARAM_STR:
 			*s_dst = param->v.str;
 			*flags |= PARAM_STR;
 			break;
 		case FPARAM_AVP:
 			avp = search_first_avp(param->v.avp.flags, param->v.avp.name,
 									&val, 0);
 			if (unlikely(!avp)) {
c6a1ba6d
 				LM_DBG("Could not find AVP from function parameter '%s'\n",
8a334551
 						param->orig);
 				return -1;
 			}
 			if (avp->flags & AVP_VAL_STR) {
 				*s_dst = val.s;
 				*flags |= PARAM_STR;
 				if (str2int(&val.s, (unsigned int*)i_dst) < 0) {
153ab772
 					LM_ERR("Could not convert AVP string value to int\n");
8a334551
 					return -1;
 				}
 			} else {
 				*i_dst = val.n;
 				*flags |= PARAM_INT;
 			}
 			break;
 		case FPARAM_SELECT:
 			ret = run_select(&tmp, param->v.select, msg);
 			if (unlikely(ret < 0 || ret > 0)) return -1;
 			if (unlikely(str2int(&tmp, (unsigned int*)i_dst) < 0)) {
153ab772
 				LM_ERR("Could not convert select result to int\n");
8a334551
 				return -1;
 			}
 			*flags |= PARAM_INT;
 			break;
 		case FPARAM_PVS:
 			if (likely(pv_get_spec_value(msg, param->v.pvs, &pv_val)==0)) {
 				if ((pv_val.flags&(PV_VAL_NULL|PV_VAL_INT))==PV_VAL_INT){
 					*i_dst=pv_val.ri;
 					*flags |= PARAM_INT;
 				}
 				if ((pv_val.flags&(PV_VAL_NULL|PV_VAL_STR))==PV_VAL_STR){
 					*s_dst=pv_val.rs;
 					*flags |= PARAM_STR;
 				}
 			}else{
153ab772
 				LM_ERR("Could not get PV\n");
8a334551
 				return -1;
 			}
 			break;
 		case FPARAM_PVE:
 			s_dst->s=pv_get_buffer();
 			s_dst->len=pv_get_buffer_size();
 			if (unlikely(pv_printf(msg, param->v.pve, s_dst->s, &s_dst->len)!=0)){
153ab772
 				LM_ERR("Could not convert the PV-formated string to str\n");
8a334551
 				s_dst->len=0;
 				return -1;
 			}
 			*flags |= PARAM_STR;
 			break;
 	}
 
 	/* Let's convert to int, if possible */
 	if (!(*flags & PARAM_INT) && (*flags & PARAM_STR) && str2sint(s_dst, i_dst) == 0)
 		*flags |= PARAM_INT;
 
 	if (!*flags) return -1;
 
 	return 0;
 }
 
44c65404
 /**
  * Retrieve the compiled RegExp.
  * @return: 0 for success, negative on error.
  */
 int get_regex_fparam(regex_t *dst, struct sip_msg* msg, fparam_t* param)
 {
 	switch (param->type) {
 		case FPARAM_REGEX:
 			*dst = *param->v.regex;
 			return 0;
 		default:
153ab772
 			LM_ERR("unexpected parameter type (%d), instead of regexp.\n", 
44c65404
 					param->type);
 	}
 	return -1;
 }
8f3db98b
 
 
 
6a40b4bd
 /** generic free fixup function for "pure" fparam type fixups.
  * @param  param - double pointer to param, as for normal fixup functions.
  * @param  param_no - parameter number, ignored.
  * @return 0 on success (always).
  */
 int fixup_free_fparam_all(void** param, int param_no)
 {
 	fparam_free_restore(param);
 	return 0;
 }
 
 
 
 /** generic free fixup function for "pure"  first parameter fparam type fixups.
  * @param  param - double pointer to param, as for normal fixup functions.
  * @param  param_no - parameter number: the function will work only for
  *                     param_no == 1 (first parameter).
  * @return 0 on success (always).
  */
 int fixup_free_fparam_1(void** param, int param_no)
 {
 	if (param_no == 1)
 		fparam_free_restore(param);
 	return 0;
 }
 
 
 
 /** generic free fixup function for "pure"  2nd parameter fparam type fixups.
  * @param  param - double pointer to param, as for normal fixup functions.
  * @param  param_no - parameter number: the function will work only for
  *                     param_no == 2 (2nd parameter).
  * @return 0 on success (always).
  */
 int fixup_free_fparam_2(void** param, int param_no)
 {
 	if (param_no == 2)
 		fparam_free_restore(param);
 	return 0;
 }
 
 
 
8f3db98b
 /** returns true if a fixup is a fparam_t* one.
0b52bcff
  * Used to automatically detect "pure" fparam fixups that can be used with non
8f3db98b
  * contant RVEs.
  * @param f - function pointer
  * @return 1 for fparam fixups, 0 for others.
  */
 int is_fparam_rve_fixup(fixup_function f)
 {
 	if (f == fixup_var_str_12 ||
 		f == fixup_var_str_1 ||
 		f == fixup_var_str_2 ||
a5b499a4
 		f == fixup_var_pve_str_12 ||
 		f == fixup_var_pve_str_1 ||
 		f == fixup_var_pve_str_2 ||
8f3db98b
 		f == fixup_var_int_12 ||
 		f == fixup_var_int_1 ||
 		f == fixup_var_int_2 ||
 		f == fixup_int_12 ||
 		f == fixup_int_1 ||
 		f == fixup_int_2 ||
 		f == fixup_str_12 ||
 		f == fixup_str_1 ||
0b52bcff
 		f == fixup_str_2 ||
 		f == fixup_regex_12 ||
 		f == fixup_regex_1 ||
 		f == fixup_regex_2
 		)
8f3db98b
 		return 1;
 	return 0;
 }
0b52bcff
 
 
a7524985
 /**
  * @brief returns the corresponding fixup_free* for various known fixup types
  * 
  * Returns the corresponding fixup_free* for various known fixup types.
0b52bcff
  * Used to automatically fill in free_fixup* functions.
a7524985
  * @param f fixup function pointer
  * @return free fixup function pointer on success, 0 on failure (unknown
  * fixup or no free fixup function).
0b52bcff
  */
 free_fixup_function get_fixup_free(fixup_function f)
 {
 	free_fixup_function ret;
 	/* "pure" fparam, all parameters */
 	if (f == fixup_var_str_12 ||
a5b499a4
 		f == fixup_var_pve_str_12 ||
0b52bcff
 		f == fixup_var_int_12 ||
 		f == fixup_int_12 ||
 		f == fixup_str_12 ||
 		f == fixup_regex_12)
 		return fixup_free_fparam_all;
 	
 	/* "pure" fparam, 1st parameter */
 	if (f == fixup_var_str_1 ||
a5b499a4
 		f == fixup_var_pve_str_1 ||
0b52bcff
 		f == fixup_var_int_1 ||
 		f == fixup_int_1 ||
 		f == fixup_str_1 ||
 		f == fixup_regex_1)
 		return fixup_free_fparam_1;
 	
 	/* "pure" fparam, 2nd parameters */
 	if (f == fixup_var_str_2 ||
a5b499a4
 		f == fixup_var_pve_str_2 ||
0b52bcff
 		f == fixup_var_int_2 ||
 		f == fixup_int_2 ||
 		f == fixup_str_2 ||
 		f == fixup_regex_2)
 		return fixup_free_fparam_2;
 	
 	/* mod_fix.h kamailio style fixups */
 	if ((ret = mod_fix_get_fixup_free(f)) != 0)
 		return ret;
 	
 	/* unknown */
 	return 0;
 }