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


Advertisements

OpenSIPS Management Interface (C Development)

.

Introduction

OpenSIPS Management Interface (MI)  is a mechanism which enables the external application (e.g. command line :opensipsctl , web: OpenSIPS-CP) to send commands (MI commands) to OpenSIPS. It is a pull based mechanism which means when you need information you need to query OpenSIPS (i.e. need to do something at a certain time). The MI command allows you to fetch/push data from/to OpenSIPS or trigger some actions there. The core has its own exported MI functions and the module has its own as well. Here you can find some examples of sending mi commands by the external applications “opensipsctl” and “OpenSIPS-CP”

Several transport protocols are used to carry MI commands and their replies between the external application and OpenSIPS. Each protocol is provided by separate OpenSIPS module. The current protocols are mi_fifo, mi_datagram, mi_xmlrpc, mi_http, mi_json, and mi_xmlrpc_ng. These modules require extra processes to avoid disturbing OpenSIPS main processes that are working with SIP.

The module must be loaded (i.e. configured to be loaded in the routing script) so its exported MI functions (exported by the module interface “exports”) are populated and can be called from the external application otherwise you will get an error message “500 command ‘Module_Name’ not available”. To be able to send MI command from the external application, the transport module also must be configured in the routing script to be loaded. For example if you want to connect to MI interface via FIFO file stream, the module MI_FIFO must be right configured and loaded. The same for the rest of transport protocols.

So two modules are needed to be able to call a specific MI function:

  • The module which exports the MI function.
  • The transport protocol which will transport the command to OpenSIPS.

The extra processes that are required for transport issues will be listening on different ports than the SIP ports. OpenSIPS can use multiple transport protocols at the same time (In the routing script, configure them to be loaded).

MI Command Syntax

If you are willing to write an external management application, you have to implement the transport you want to use. Your application will behave like a client which sends MI command in a specific format to OpenSIPS. These are the current formats:

MI Internal Structure

The following is the module_exports structure (Module Interface) defined in the file “sr_module.h” with the parts related to MI.

struct module_exports {.

……… /* Many Fields are Missed For Simplicity */

mi_export_t*     mi_cmds;           /* Array of the exported MI functions */

….

proc_export_t*  procs;     /* Array of the additional processes required by the module */

……

}

procs

Sometime the module needs extra processes like the transport protocol. So it exports

  • The number of required processes to be forked (“no” number).
  • The helper functions (pre_fork_function and post_fork_function) which help the attendant process to create the extra processes.
  • The function which will be executed by the extra processes. What will be done by these extra processes will not interfere with the rest of processes that handle the SIP.

The structure is defined in the file “sr_module.h” in OpenSIPS source directory:

typedef struct cmd_export_ cmd_export_t;

struct proc_export_ {
char *name;
mod_proc_wrapper pre_fork_function;
mod_proc_wrapper post_fork_function;
mod_proc function;
unsigned int no;
unsigned int flags;
};

typedef void (*mod_proc)(int no);

typedef int (*mod_proc_wrapper)();

The flags can be 0 or PROC_FLAG_INITCHILD. If PROC_FLAG_INITCHILD is provided, the function “child_init” from all modules will be run by the new extra processes.

Example of procs NULL terminated array:

static proc_export_t mi_procs[] = {
{“MI Datagram”,  pre_datagram_process,  post_datagram_process,
datagram_process, MI_CHILD_NO, PROC_FLAG_INITCHILD },
{0,0,0,0,0,0}
};
static param_export_t mi_params[] = {
{“children_count”,      INT_PARAM,    &mi_procs[0].no},…….}

mi_cmds

mi_cmds is an array of exported MI function (Type: mi_export_t*). The definition of the type “mi_export_t” is included in the file “mi/mi.h” as following:

typedef struct mi_export_ {
char *name;
char *help;
mi_cmd_f *cmd;
unsigned int flags;
void *param;
mi_child_init_f *init_f;
} mi_export_t;

name is the actual name of the MI function which will be called. help is the information about what this function is doing. cmd is a pointer to the actual mi function:

typedef struct mi_root* (mi_cmd_f)(struct mi_root*, void *param)

The return value is mi_root * (pointer to the root of the MI reply tree).  The first parameter is the MI root parameter(type: mi_root *) and the second parameter is the actual command parameter (void *). the param is the actual parameter of the MI function.

init_f is the child init function for the exported MI function. In some parts, some extra stuffs are needed to be done by the MI workers (processes that are handling the MI requests  like MI_FIFO proces, MI_XMLRPC process,….. This function will be called at the startup after OpenSIPS has been forked (will be called one time by each core process individually).

Example of MI functions NULL terminated array:

static mi_export_t mi_cmds[] = {
{ “mi_get_function“,”This Function is doing bla bla”,mi_get_function,MI_NO_INPUT_FLAG,0,0},
{0,0,0,0,0,0}
}

The string “mi_get_function” is the actual name of the mi command which will be named by when MI command is received and needed to be verified (lookup_mi_cmd(MethodName,..)). The actual function will be like this:

struct mi_root* mi_get_function(struct mi_root*  root, void * param){

……..

}

MI Function Reply Tree

The return value of MI function is a pointer to a tree (reply tree) . The root of this tree is pointer to struct mi_root. This structure is defined in the file “mi/tree.h”:

struct mi_root {
unsigned int  code;
str   reason;
struct mi_handler  * async_hdl;
struct mi_node node;
}

The code is the root code of the response (200 for sucess,500 for error, …) and the node is the starting node (type: mi_node defined in the same file “mi/tree.h”):

struct mi_node {
str value;
str name;
unsigned int flags;
struct mi_node *kids;
struct mi_node *next;
struct mi_node *last;
struct mi_attr *attributes;
}

To initialize MI reply tree in the previous MI function (mi_get_function), the function “init_mi_tree” can be called. To add child node, the function “add_mi_node_child“. To add attribute, the function “add_mi_attr” is called  and so on. Here you can find an example code where a reply tree is built when a MI function is called.

struct mi_root* mi_get_function(struct mi_root*  root, void * param){

………

struct mi_root * rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));    /* 200 OK Reply */

……..

}

The transport process like datagram_process, xmlrpc_process,..etc  behaves as a server which accepts connections (UDP connections in case datagram transport , TCP connections in case xmlrpc connections and so on). To serve the MI request, the transport process calls functions from OpenSIPS core (Management Interface: “mi/mi.h”) in addition to some module-defined functions.

When the message is received, the transport process checks if the requested MI function is available (looks up the MI command):

struct mi_cmd*  fu =lookup_mi_cmd((char*)methodName, strlen(methodName))

The “lookup_mi_cmd” is defined in “mi/mi.h”. If “f==0”, the MI command is not available. Otherwise it parses the parameters of the requested MI function into a MI tree. For example in xmlrpc, the function “xr_parse_tree” is defined in the file “modules/mi_xmlrpc/xr_parser.h” and called in “modules/mi_xmlrpc/xr_server.c”. It returns a pointer to the reply tree (mi_root * t).

After this, the actual mi function will be called through the function “run_mi_cmd”. As i explained above the return value of the MI function (reply tree) has the type mi_root*. This is how it is called in xmlrpc transport module “”.

struct mi_root*  mi_rpl=run_mi_cmd( fu , t , (mi_flush_f*)xr_flush_response,env))

The function “run_mi_cmd” is defined in the file “mi/mi.h” and in its body you call the actual MI function as following:

static inline struct mi_root* run_mi_cmd(struct mi_cmd *cmd, struct mi_root * t, mi_flush_f *f, void *param){

struct mi_root *ret;

…..

ret = cmd->f( t, cmd->param);

…..

}

struct mi_cmd {
int id;
str module;
str name;
str help;
mi_child_init_f *init_f;
mi_cmd_f *f;
unsigned int flags;
void *param;
};

Then the reply tree mi_rpl  (Type: mi_root *) will be formatted corresponded to the transport process (transport protocol). For example if the protocol is xmlrpc, then the reply tree will be formatted in xml either as string that contains the name and attributes of each node OR as an array where each element contains a node information (node name and its attributes). Each attribute has name and value.

Finally, the response will be written in fifo file, UDP socket (datagram transport), TCP socket (xmlrpc transport) so it will be sent to the external application.

Test MI Function of a New Module

You can test the MI command by using opensipsctl as following:

# scripts/opensipsctl fifo mi_get_function


 Note

  • The OpenSIPS core MI exported functions (mi_core_cmds) are defined in the file “mi_core.c”.
  • The OpenSIPS Module MI exported functions (mi_cmds) are defind in the file modules/Module_Name/Module_Name.c
  • OpenSIPS has many transport modules. Go to “modules/mi_Transport“, Where Transport can be datagram, xmlrpc, json,…etc.

 More Information