https://voipmagazine.wordpress.com/2015/01/07/opensips-module-interface-part-1-c-development/
OpenSIPS consists of two parts: the core and the modules. OpenSIPS module is a shared library (.so file extension) which is configured in the routing script and loaded at OpenSIPS startup time (the loader: /usr/bin/ld). The name of the module is the name of the shared library.
To open and edit the source files, I will use the text editor vim. You can install it on Fedora Red Hat: # yum install vim Or use your own favourite text editor to edit the source files. Then go to the OpenSIPS src folder (It can be “/usr/local/src/opensips_1_11”) and explore the files (# vim File-Name). To enter in write mode, press i. To paste a copied code into an opened file do it like this: press ESC and then write :set paste. To save a file press ESC and them write :wq
If you are reading in a source file and you want to open a file (i.e. the file where its name comes after the “#include” statement), put the curser on the file name and press either “gf” to go to the file or “ctrl-w f” to open the file in a new window.
In this part i will talk about the definition of the module interface, some fields of its structure, and how to export function from the module to the routing script. So you can call this function from the routing script. Also i will talk about exporting a module parameter.
OpenSIPS module interface is called module_exports and it is used by OpenSIPS core to serve the module. Each OpenSIPS module creates a variable of this type and call it by “exports”. When OpenSIPS tries to load the module, it takes the module path from the configuration file (loadmodule statement), open the shared object file (.so file), and try to find the “exports” which populates everything about the module (e.g. script functions, parameters, mi functions, ..etc.).
The structure of the module interface (module_exports) is defined in the header file “/usr/local/src/opensips_1_11/sr_module.h”. It is a struct C data type which has the declared name module_exports. It defines a grouped list of fields that are populated by the module. I will explain some of these fields and leave the rest to the next article.
struct module_exports {
. char* name; /* 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; /* Array of the exported commands */
. param_export_t* params; /* Array of the exported module parameters */
. … /*Rest of Fields*/
}
name: It is the module name. If the module will be used, its name must be passed to the function “loadmodule” in the configuration file. The corresponding shared library (shared object) will be “new_module.so” and it will be loaded at the startup time. version: It is the module version. It is used to detect the conflict situations where you have a module with version different from the core version. In the same file “sr_module.h”, we have the defined variable “MODULE_VERSION” which is OPENSIPS full version concatenated with the OpenSIPS compilation flags.
#define MODULE_VERSION OPENSIPS_FULL_VERSION, OPENSIPS_COMPILE_FLAGS
This variable can be set to the field version in the variable “exports” which has the type module_exports and represents the actual module interface. This is shown further below.
compile_flags: You can skip this if you have used the MODULE_VERSION which already has the compile flags after the comma “,“. Otherwise use them independently. Remember when you define a variable of struct in c language, the “,” is used to separate between the fields.
dlflags: In the same file “sr_module.h”, we have the defined variable DEFAULT_DLFLAGS which has the value 0 which means instructs the module loader to use OpenSIPS default dlopen flags.
#define DEFAULT_DLFLAGS 0
This variable can be set to the field dlflags in the actual interface “exports”.
cmds which has the OpenSIPS-defined C-type cmd_export_t*. It is an array of functions that are exported to the routing script. It is defined in the same file “sr_module.h”
typedef struct cmd_export_ cmd_export_t;
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” the parameters */
free_fixup_function free_fixup; /* pointer to the function called to free the “fixed” parameters */
int flags; /* Function flags */
}
The structure above defines a function in a dynamic and generic way. The name is the function actual name which will be used from the script when calling the function. The function is a pointer to the C code of the function which will be actually called. This pointer is defined as following:
typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*);
The return value of the function is integer (int). “cmd_function” is the pointer to the function. The rest are the parameters of the function (First parameter is the actual SIP message which triggers the processing and the rest are multiple char pointers. You can have till sex arguments). In the module C-code, you will have something like this:
int new_function(struct sip_msg *msg,char *param) {
……… /* Actual Exported Function Code*/
/* You can manipulate the SIP message Here:
/* Remove/Add header Fields …….*/
}
This function has one “char *” parameter but as i said you can have up to 6 “char *” parameters. The return value of the exported function can be either a positive number (higher than +1) on success or lower than -1 on failure. The return value must not be zero because this will exit the script.
The param_no is the number of parameters passed to the function. If this number is equal to zero, this means there are no parameters for the function. But keep in mind that the first parameter (SIP message) will always be received as a first parameter of the C code function even if you call the function with no parameters from the routing script (Example new_function() in the script will be something like new_function(msg) in the C code).
These parameters must be evaluated (get the length of each “char *” parameter) and probably processed. To avoid CPU penalty of doing this evaluation and processing many times (For each function call), the evaluation and processing can be done only once at the OpenSIPS startup via fixup function. This reduces the runtime evaluations.
fixup is a pointer to the function which will be called to fix the parameters of the exported function. The structure of fixup pointer is “fixup_function” which is defined in the file “sr_module.h” as following:
typedef int (*fixup_function)(void** param, int param_no);
The fixup function will receive the current parameter to be processed (param) and its index (param_no). It is important to check the parameter index so the function knows which parameter it is currently evaluating. The actual code of the fixup function looks like this:
int do_fixup (void** param, int param_no){
/* Parameter Evaluation and probably Processing */ check the parameter you are currentlu evaluating
char * value= (char *)(*param);
str* ret_value;
………
if (param_no==1){
ret_value=pkg_malloc(sizeof(str));
……..
ret_value->s=value;
ret_value->len=strlen(value);
*param=(void* )ret_value;
return 1;
}
…….
return 0;
}
OpenSIPS 1.X has its own internal memory manager (NOT standard) and the function pkg_mallocallocates a memory of a given size (here sizeof(str)) and return a pointer to the allocated memory (here ret_value is a pointer to str). Note that the received parameter (param) is replaced with the evaluated and processed value (ret_value). After the fixup function has been called, the exported function will have the param fixed (type = str not char*) but casting is still required ((str *)param).
Using fixup function is not restricted to constants. It can be also used with variables.
free_fixup is a pointer to the function which is used to free up the fixed parameters (free up the resources used for the fixed parameters).
flags represents the places in the routing script where the function is available and can be called. For example it can be called from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, or LOCAL_ROUTE (these routes are defined in the header file “route.h”). If the function is called in forbidden place in the routing script, you will get an error message and OpenSIPS will not start.
The figure below is a graphical representation of the module interface:
When you create a new module, you need to create a variable of the type struct module_exports which will be the actual interface of the new module. Assume you are writing the module “new_module”, you will add its interface (named “exports”) in its C code. It could be like this:
struct module_exports exports= {
. “new_module”, /* module name */
. MODULE_VERSION, /* Module Version
. DEFAULT_DLFLAGS, /* dlopen flags */
. cmds, /* exported functions */
. params, /* exported parameters */
…. rest of values
}
For example if the module has two functions and you want to export these functions to the routing script, add them to an array of the type cmd_export_t and make the array null terminated ({0,0,0,0,0,0}). As an example assume the first function with one argument and the second function without any argument (same name will be used in the routing script but different names in the C code).
static cmd_export_t cmds[]={
{“new_function“,(cmd_function)new_function,1,do_fixup,0, REQUEST_ROUTE|ONREPLY_ROUTE},
{“new_function“,(cmd_function)new_function0,0,0,0, REQUEST_ROUTE|ONREPLY_ROUTE},
{0,0,0,0,0,0}
}
In the routing script, these functions can be called only within REQUEST_ROUTE or ONREPLY_ROUTE. Otherwise as i mentioned above, you will get an error messages and OpenSIPS will not start.
The fixup function “do_fixup” is populated here so it will fix up the only one parameter of the exported function “new_function”.
Somewhere in the module C code, You will write the bodies of the actual exported functions: The first function “new_function” which has only one parameter to be passed from the script will look like this:
int new_function(struct sip_msg *msg,char *param){
…….
/* The do_fixup is the fixup function of this function, so we can cast the param to str * as it is evaluated there */
str * param_value = (str *)param;
……..
}
The second function is new_function0 which has no parameters when it is called from the routing script.
int new_function0(struct sip_msg *msg){
…….
}
The field “params” in the definition of the module interface is an array of parameters which are exported from the module to the routing script so they can be used in the script and populated by script parser. The exported parameter can be set from the routing script. The OpenSIPS-defined C-Type of the exported parameter is “param_export_t” and it is defined in the file “sr_module.h” as following:
typedef struct param_export_ param_export_t;
struct param_export_ {
char* name; /*!< null terminated param. name */
modparam_t type; /*!< param. type */
void* param_pointer; /*!< pointer to the param. memory location */
}
The field name is the name of the parameter which will be used in the script. The type can be:
USE_FUNC_PARAM
) or (INT_PARAM|USE_FUNC_PARAM
). This means there is a function will be called every time the parameter is set.The field param_pointer which is a pointer to either the memory location of the parameter or a pointer to the function which will be called on the value given from the routing script.
All exported parameters will be grouped in “params” in “exports” (actual interface). Remember the OpenSIPS-defined C-Type of the params array is param_export_t*. For example it can be:
static param_export_t params[]={
{ “new_param”, STR_PARAM|USE_FUNC_PARAM, (void *)set_function},
{ “db_url”, STR_PARAM, &db_url.s},
{0,0,0}
}
The name of the first exported parameter is the string “new_param”. The type of this parameter is “STR_PARAM|USE_FUNC_PARAM” (i.e. “|” is a bit mask). The function “set_function” will be called each time the parameter “new_param” is set. It look like this:
int set_function(unsigned int type, void *val){
…….
}
In the routing script this parameter can be set as following: modparam(“new_module”,”new_param”,”Test Value”) and the function “set_function” will get the value of “new_param” parameter (“Test Value”) as its argument.
Start playing with OpenSIPS Source code (read in the code, make some changes, write log statements (e.g. LM_INFO(….)), recompile&start OpenSIPS, and read the log and see the results). If you want to build your own module, I suggest you to take as start step the OpenSIPS Example which is available on the OpenSIPS web site and make any OpenSIPS module like dialog module (Files: dialog.c and dialog.h) as a reference to your code (follow the same strategy).