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


Compilation, Installation, and Configuration of OpenSIPS’s Perl Module

.

If you want to implement a new feature (i.e. not exist in OpenSIPS), you can extend OpenSIPS by either writing new module (in C) or writing Perl script and call it within the routing script using OpenSIPS’s Perl module. In this article i will explain how to compile, install and configure OpenSIP’s Perl module. Then you will be ready to write your own extension.  To do that, follow these steps.

Installation of “ExtUtils::Embed” Perl Module

It is Perl module used for embedding Perl interpreter and extensions in C/C++ (i.e. Cross Compiling). To install this module on Fedora: #yum install perl-ExtUtils-Embed. The Makefile of the application can call functions from this module while building the application. In OpenSIPS, we will call the functions manually and set the results in environment variables accessed by the Makefile. One of these functions is “ldopts” which gives the GCC ‘s arguments for linking the perl library to  C/C++ application (Perl is dynamically linked to OpenSIPS). You can call this function as following:

# perl –MExtUtils::Embed –e ldopts.

Set the option -M to specify the module name which is “ExtUtils::Embed” and the option -e to call the function “ldopts”.

Pre Compilation Procedures

OpenSIPS is C application. C is a compiling language whereas Perl is a scripting language with C++ interpreter. OpenSIPS uses Perl interpreter and extensions in its Perl module to Parse/Load/Execute Perl scripts. So we need  to prepare  GCC (C/C++ compiler) to be able to compile OpenSIPS’ s Perl module. In this tutorial i am using perl (v5.18.4) and GCC (v4.8.3) on Fedora 20.

  • Execute the command #perl -MExtUtils::Embed -e ldopts.

In my case the output is:

-Wl,–enable-new-dtags  -fstack-protector  -L/usr/lib64/perl5/CORE -lperl -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc

Assign the output to the environment variable PERLLDOPTS:

#export PERLLDOPTS=-Wl,–enable-new-dtags  -fstack-protector  -L/usr/lib64/perl5/CORE -lperl -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc.

To make this permanently, edit the file “.bash_profile” and write the previous export statement there. Check its value: # echo $PERLLDOPTS

  • Execute the command #perl -MExtUtils::Embed -e ccopts.

In my case the output is:

-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64  -I/usr/lib64/perl5/CORE

Assign the output to the environment variable PERLCCOPTS. Check its value : # echo $PERLCCOPTS

  • Execute the command : #echo “`perl -MConfig -e ‘print $Config{installprivlib}’`/ExtUtils/typemap” and assign the output to environment variable TYPEMAP.
  • OpenSIPS’s Makefile is already prepared for cross compilation. It will recognize these variables. For example “Makefile.defs-> CROSS_COMPILE ?=” will include PERLCCOPTS‘s value (DON’T DO ANYTHING).

Notes:

  • If you write these variables in bash profile (.bash_profile), you must reload the file: # source .bash_profile
  • See the corresponding procedures in your system.

Compilation & Installation of OpenSIPS’s Perl Module

  • If you don’t have OpenSIPS yet, have a look  here. Include Perl in the compilation (i.e. Click on “Configure Compile Options” –> “Configure Excluded Modules”. Then compile the OpenSIPS by selecting “Compile and Install OpenSIPS”. This will not rebuild everything only the new selected modules.

OR

From the OpenSIPS ‘s src  folder (“/usr/local/src/opensips_1_11”), execute:

#make include_modules=”perl”modules

# make install

  • To see if the module is compiled and installed , check the Perl module path “/usr/local/opensips_1_11/lib64/opensips/perl/”. Check also Perl.so in the OpenSIPS’s C modules path: In my case  “ /usr/local/opensips_1_11/lib64/opensips/modules/“.

Configuration of OpenSIPS’s Perl Module

♣ The module “Perl” must be loaded in OpenSIPS’s routing script (i.e. the configuration file) after loading the module “sl”.  To load the module, write this statement:

loadmodule “perl.so.

♣ Set the name of the new extension (i.e. the name of your Perl script):

modparam(“perl”, “filename”, “/usr/local/opensips_1_11/Your-Extensions-Folder/Name-Of-Your-Script.pl”)

♣ Set the module path

modparam(“perl”, “modpath”, “/usr/local/opensips_1_11/lib64/opensips/perl/”)

♣ Restart OpenSIPS: #systemctl restart opensips.service

♣ Check the permission to execute the script: # ls -l  /Path-To-Your-Script/Name-Of-Your-Script.pl. (  use “chmod +x” to give it execute permission).

♣ Use “strace”  to debug the script execution. # strace file Name-Of-Your-Script.

Writing a New OpenSIPS ‘s Perl Extension

Later i will write a Perl script that extend the feature “Advice of Charge (AOC) in SIP”. If you are in hurry you can go here and see an example ( Replace 183 early media reply with 180 (Ringing)). Now you can write your Perl script and call functions from OpenSIPS’s Perl module. For example the package Message  provides access functions for an OpenSIPS sip_msg structure. For example calling the function getRURI() gives the Request-URI of the current message.


More Information