编写module需要用到的各种数据结构定义在sr_module.h。其中比较重要的是cmd_export_t,用于声明这
个module的导出函数
struct cmd_export_ {char * name ; /* null terminated command name */cmd_function function ; /* pointer to the corresponding function */int param_no ; /* number of parameters used by the function */fixup_function fixup ; /* pointer to the function called to "fix" theparameters */free_fixup_functionfree_fixup ; /* pointer to the function called to free the"fixed" parameters */int flags ; /* Function flags */};
第一个参数是该module导出到opensips脚本中的函数名
第二个参数是对应的具体执行函数。cmd_function的函数定义为
typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*);
从第二个参数起都是字符串。具体使用多少个参数由结构体第三个参数决定
对于cmd_function,返回值小于0表示函数执行出错,返回值等于0会使该次脚本执行结束;返回值大于0
表示函数执行正常
第四个参数fixup function,主要用于将脚本传入的参数转换成int、正则表达式等其他种类的参数。
opensips已经定义好了常用的类型转换,定义在mod_fix.h中,一般直接调用现成的即可以
第五个参数free_fixup_function,用于释放fixup function执行时申请的内存
第六个参数,用于描述该导出函数可以在哪段路由中被执行。可选的值包括
REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, LOCAL_ROUTE等
另外一个比较重要的结构体是
struct module_exports {char * name ; /*!< null terminated module name */char * version ; /*!< module version */char * compile_flags ; /*!< compile flags used on the module */unsigned int dlflags ; /*!< flags for dlopen */cmd_export_t * cmds ; /*!< null terminated array of the exportedcommands */param_export_t * params ; /*!< null terminated array of the exportedmodule parameters */stat_export_t * stats ; /*!< null terminated array of the exportedmodule statistics */mi_export_t * mi_cmds ; /*!< null terminated array of the exportedMI functions */pv_export_t * items ; /*!< null terminated array of the exportedmodule items (pseudo-variables) */proc_export_t * procs ; /*!< null terminated array of the additionalprocesses reqired by the module */init_function init_f ; /*!< Initialization function */response_function response_f ; /*!< function used for responses,returns yes or no; can be null */destroy_function destroy_f ; /*!< function called when the module shouldbe "destroyed", : on opensips exit */child_init_function init_child_f ; /*!< function called by all processesafter the fork */};
char* name: 模块的名字
char *version: 一般都把值设为MODULE_VERSION
char *compile_flags: 一般都把值设为DEFAULT_DLFLAGS
接着几个参数设置模块的导出函数、导出参数、MI命令等
proc_export_t* procs: 定义模块自己的独立运行进程
init_function init_f: 模块初始化化函数,只在opensips启动的时候被执行一次
response_function response_f: 很少有模块需要用到,尚不清楚有什么用
destroy_function destroy_f: 模块销毁时被调用
child_init_function init_child_f: 模块在每个子进程中的初始化函数。因为opensips的架构是多进
程的,像数据库连接这些必须在每个子进程内自己创建一份
对编写module有个基本的认识以后,可以开始着手写一个简单的module
1、在opensips/modules目录下新建一个目录test
2、编写这个test module的Makefile。如果这个module不需要连接其他额外的库,Makefile是相当的简
单,只要把NAME后面生成库的名字改一下
include ../../ Makefile . defsauto_gen =NAME = test . soLIBS =include ../../ Makefile . modules
3、编写这个模块的主体代码
#include "../../sr_module.h"static int mod_init ( void );/* Exported functions */static int my_test ( struct sip_msg * _msg , char * param );static cmd_export_t cmds [] = {{ "my_test" , ( cmd_function ) my_test , 1 , 0 , 0 ,REQUEST_ROUTE },{ 0 , 0 , 0 , 0 , 0 , 0 }};/* Module interface */struct module_exports exports = {"test" , /* module name*/MODULE_VERSION ,DEFAULT_DLFLAGS , /* dlopen flags */cmds , /* exported functions */0 , /* module parameters */0 , /* exported statistics */0 , /* exported MI functions */0 , /* exported pseudo-variables */0 , /* extra processes */mod_init , /* module initialization function */0 , /* response function */0 , /* destroy function */0 , /* per-child init function */};static int my_test ( struct sip_msg * _msg , char * param ){LM_INFO ( "Receive message %s\n" , param );return 1 ;}static int mod_init ( void ){LM_INFO ( "initializing...\n" );return 0 ;}
4、建立好这个模块的目录之后,opensips就会去编译这个模块,除非你在的
exclude_modules中加入了这个模块的名字。按照正常opensips的编译步骤进行编译
5、把新编写的模块加入路由之中
loadmodule "msg_trace.so"
在路由的某个地方插入
my_test("1234");
可以看到路由被执行到时,日志有输出
Aug 2 06:20:45 simope-0A ./opensips[2346]: INFO:test:my_test: Receive message 1234
主要参考textops、cachedb_local、cfgutils等较为简单的module
另外也可以参考同源的kamailio项目,虽然结构上也有细微的变换,但是kamailio的开发文档相对比较
齐全
/pub/kamailio-devel-guide/#c16moduledev