OpenSIPS Module Interface – Part 2 (C Development)

.

Introduction

This is Part 2 of the topic “OpenSIPS Module Interface”. In Part 1,I have talked 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 in the routing script. Also i have talked about how to export a module parameter. I have talked about the OpenSIPS management interface in the article “OpenSIPS Management Interface (C Development)“. In this article, i will explain a little bit in the module interface and i will show you how to compile a new module.

The definition of the module interface is defined in the header file: “sr_module.h” as following:

struct module_exports {.

……… /* Fields that are explained in the Previous Article are missed here*/

……..  /* Some other fields are missed to be explained in the Next Article */

init_function   init_f;                              /* Initialization function */

destroy_function destroy_f;                /* Function called when the module

.                                                                       should be “destroyed” */
.child_init_function init_child_f;            /* Function called by all processes after the fork */

}

init_f function which will be called at OpenSIPS startup time. In OpenSIPS 1.x which is multi process application, this function will be called when OpenSIPS is just one process (attendant process). For example checking for the database connection can be done here or allocating a memory that will be as shared memory for the forked processes (after forking). You can define your init_f function in the “exports” which is the actual module interface. Lets say “mod_init” is the initialization function in the module interface:

static int mod_init(void){

……

}

The initialization of the exported variables in the routing script will be done before calling the function mod_init. The full OpenSIPS configuration file will be parsed and the own module parameters will be included. For example the parameter “db_url” which will be configured in the routing script:  modparam(“module_name”,”db_url”,”mysql://user:passwd@host/database”) and the “db_url” will be evaluated and can be used in the function “mod_init”. So a connection to the database can be opened in the body of the “mod_init” function.

Also the helper API like shared memory, locking, timer, ..etc. will be available and can be used. Note some resources like new timer processes for the module can only be initialized in the “mod_init” function.

After forking, each process will get a copy of what the OpenSIPS attendant process had before forking (structures/connections).

init_child_f function will be called after OpenSIPS has been forked (It will be called by each process individually). For example if each process will have its separate DB connection, the creation of the DB connection will be in this function.

static int child_init(int rank){

……..

}

The rank parameter allows you to know which process is calling the function. For example it can be called from SIP worker or MI worker (e.g. fifo worker).

destroy_f function will be called when OpenSIPS will shutdown. Here you can do some cleaning of resources(memory, DB connections, and so on). For example you can free the memory for some module-created variables (e.g. pkg_free(Pointer to the Variable)). OpenSIPS 1.X has its own internal memory manager and the function pkg_free is like the function free(pointer to a variable) of glibc standard library. The pointer must not be null otherwise it will crash. Also here in this function, you can save a state that the module have into persistent storage so they can be loaded again at the next OpenSIPS startup. For example in this function, the “dialog” module saves the dialog states in the database.

OpenSIPS 2.X is multi thread application. I will back to it in one or two upcoming articles.

Add Include Directives

You need to add the include statements to the new module code to be able to compile it. For example: the “string.h”, “sr_module.h” which includes the definitions of the structures, “dprint.h” which is used to print stuffs to the syslog or standard error, “mem.h” which is used to allocate/free a memory, and the header file which contains the structures of the new module (e.g. new_module.h).

How to Compile a New OpenSIPS Module

Each OpenSIPS module has its own regular Makefile. It will be run by the master Makefile. The file name is “Makefile” and its content looks like this:

include ../../Makefile.defs
auto_gen=
NAME=new_module.so
LIBS=

include ../../Makefile.modules

The “Makefile.defs” must be included from the OpenSIPS source folder in addition to the name of the shared object “new_module.so” which will be generated and the file “Makefile.modules”. If the module has external libraries dependencies, they should be linked in the module’s Makefile.  Here we have two Makefile variables:

  • DEFS: This is used if the module has additional definitions that you want to pass it to the compiler. Add this line to the module’s Makefile if the module has additional definitions:

DEFS+=-I$(LOCALBASE)/include

OR DEFS+=-I../../../exdef/include

+= is a Makefile operator.

  • LIBS: This is if the  module requires external libraries. So these options:  -llibrary and the -Lpath_to_library will be set to LIBS variable. For example for “cachedb_memcashed module”, add this line to the module’s Makefile:

LIBS=-L$(LOCALBASE)/lib -lmemcashed

To compile the module:

  • The module source files (.c and .h files) and the Makefile should be in the folder “/usr/local/src/opensips_1_11/modules/new_module”
  • From the OpenSIPS ‘s src  folder (“/usr/local/src/opensips_1_11″), execute:

# make include_modules=”new_module” modules OR # make modules modules=modules/new_module

# make install

  • To see if the module is compiled and installed , check having the file “new_module.so” installed in the subdirectory “/usr/local/opensips_1_11/lib64/opensips/modules/“.

Testing

Write log statements (e.g. LM_INFO(….)) in the C code of the new module. Compile it , and restart opensips. Open the log file and search for the name of the module (for example search for “new_module”) to see what is happening to the module “new_module”.


Notes

1- If you’ve got a compilation error (e.g “….. No rule to make target ….”) and you want to clean everything (delete the generated object files and dependencies files (files with extensions “.d”), execute “make proper” and then “make”.

2- If the module depends on external libraries, the module must NOT be compiled by default. To disable this, we edit the file “Makefile.conf.template” which specifies the modules that are not compiles by default.

  • Add a line for your module in “Makefile.conf.template” file in this format:

modulename= Module-Description | module-dependency

For example for the “xcap” module:

xcap= XCAP utility functions for OpenSIPS. | libxml-dev

  • Add the module name to “exclude_modules” list in “Makefile.conf.template” file.
  • After this compile the module as explained in this article.

3- Each OpenSIPS ‘s module has a section in its documentation called “Dependencies -External Libraries” which contains the external libraries that must be installed for this module.


Next

I will continue explaining the module interface.

More Information


OpenSIPS Module Interface – Part 1 (C Development)

.

Introduction

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

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.).

ModuleInterface

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*/
}

The fields are:

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_malloc allocates 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:

The Actual 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

}

How to Export a Function to the Routing Script ?

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){

…….

}

How to Export a Parameter to the Routing Script ?

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:

  • A regular string (Flag: STR_PARAM) or regular integer (Flag: INT_PARAM)
  • Or one of the above types (STR_PARAM or  INT_PARAM) combined with the flag USE_FUNC_PARAM (STR_PARAM|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.


Note

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).


Next

In the Next Part, i will continue explaining the rest of the module interface fields.


 More Information