Documentation |
Documentation -> Development Manual 3.1 -> Module DevelopmentThis page has been visited 3505 times. Pages for other versions: devel 3.5 3.4 Older versions: 3.3 3.2 3.1
Table of Content (hide)
1. IntroductionDue to the OpenSIPS modular architecture, the easiest way to add new features ( new parameters, script functions, MI function etc ) is to incorporate them into a new OpenSIPS module. loadmodule "mynewmod.so"
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 exported commands */ param_export_t* params; /*!< null terminated array of the exported module parameters */ stat_export_t* stats; /*!< null terminated array of the exported module statistics */ mi_export_t* mi_cmds; /*!< null terminated array of the exported MI functions */ pv_export_t* items; /*!< null terminated array of the exported module items (pseudo-variables) */ proc_export_t* procs; /*!< null terminated array of the additional processes 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 should be "destroyed", e.g: on opensips exit */ child_init_function init_child_f;/*!< function called by all processes after the fork */ };
struct module_exports exports= { "dialog", /* module's name */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ cmds, /* exported functions */ mod_params, /* param exports */ mod_stats, /* exported statistics */ mi_cmds, /* exported MI functions */ mod_items, /* exported pseudo-variables */ 0, /* extra processes */ mod_init, /* module initialization function */ 0, /* reply processing function */ mod_destroy, child_init /* per-child init function */ }; 2. Compiling a moduleFurther on, we will be following the various options we have in building our new module, named ournewmod.
# $Id$ # # WARNING: do not run this directly, it should be run by the master Makefile include ../../Makefile.defs auto_gen= NAME=ournewmod.so LIBS= include ../../Makefile.modules
include ../../Makefile.defs auto_gen= NAME=cachedb_memcached.so DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lmemcached include ../../Makefile.modules
If our new module depends on external libraries, the module must not be left to compile by default ! This must be done by editing the Makefile.conf.template file - where we specify which modules are to not be compiled by default, along with the dependencies they have. modulename= Module Description | module dependency
3. Initializing the moduleIn the context of initializing our new module, there are two types of functions that will help us : 3.1 mod_initThis function must be specified in the init_f member of our module_exports exports structure. /* MUST return 0 in case of success, anything else in case of error */ typedef int (*init_function)(void);
Since this function is called from the context of only one process, after OpenSIPS forks, each OpenSIPS process will receive a copy of what the attendat process had. 3.2 child_initThis function must be specified in the init_child_f member of our module_exports exports structure. /* MUST return 0 in case of success, anything else in case of error */ typedef int (*child_init_function)(int rank);
#define PROC_MAIN 0 /* Main opensips process */ #define PROC_TIMER -1 /* Timer attendant process */ #define PROC_MODULE -2 /* Extra process requested by modules */ #define PROC_TCP_MAIN -4 /* TCP main process */ #define PROC_BIN -8 /* Any binary interface listener */
If we must do time consuming operations ( eg. load many rows from a database ) , we should be doing this inside the child_init() function for a single process ( eg. rank == 1 would be the context of our first UDP listener) , instead of the mod_init() function.
4. Destroying the moduleThis function must be specified in the destroy_function member of our module_exports exports structure. typedef void (*destroy_function)(); 5. Adding module ParametersAdding new module parameters is done by populating the params member in our module's exports structure. At OpenSIPS startup, OpenSIPS will parse the provided script and set our internal variables accordingly to what the OpenSIPS script writer has configured. The parameter definition ( param_export_t ) is the following : struct param_export_ { char* name; /*!< null terminated param. name */ modparam_t type; /*!< param. type */ void* param_pointer; /*!< pointer to the param. memory location */ };
int enable_stats = 0; static str db_url = {NULL,0}; static param_export_t mod_params[]={ { "enable_stats", INT_PARAM, &enable_stats }, { "db_url", STR_PARAM, &db_url.s }, { 0,0,0 } }
loadmodule "ournewmod.so" modparam("ournewmod","enable_stats", 1) modparam("ournewmod","db_url","mysql://vlad:mypw@localhost/opensips")
static param_export_t params[]={ { "cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection}, {0,0,0} }; int set_connection(unsigned int type, void *val) { LM_INFO("Our parameter has been set : value is %s\n",(char *)val); /* continue processing, eg : add our new parameter to a list to be further processed */ } 6. Adding module FunctionsAdding new module parameters is done by populating the cmds member in our module's exports structure. 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 */ };
In order to overload a particular function, you can simply list it twice with the same name in the cmds structure, but change the param_no field. A script function exported by a module has the following definition : typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*);
The flags member in the cmd_export_ structure dictates where within the OpenSIPS script can that particular function be called. Current options here are : #define REQUEST_ROUTE 1 /*!< Request route block */ #define FAILURE_ROUTE 2 /*!< Negative-reply route block */ #define ONREPLY_ROUTE 4 /*!< Received-reply route block */ #define BRANCH_ROUTE 8 /*!< Sending-branch route block */ #define ERROR_ROUTE 16 /*!< Error-handling route block */ #define LOCAL_ROUTE 32 /*!< Local-requests route block */ #define STARTUP_ROUTE 64 /*!< Startup route block */ #define TIMER_ROUTE 128 /*!< Timer route block */ #define EVENT_ROUTE 256 /*!< Event route block */
{"lb_is_destination",(cmd_function)w_lb_is_dst4, 4, fixup_is_dst, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
static int fixup_is_dst(void** param, int param_no) { if (param_no==1) { /* the ip to test */ return fixup_pvar(param); } else if (param_no==2) { /* the port to test */ if (*param==NULL) { return 0; } else if ( *((char*)*param)==0 ) { pkg_free(*param); *param = NULL; return 0; } return fixup_pvar(param); } else if (param_no==3) { /* the group to check in */ return fixup_igp(param); } else if (param_no==4) { /* active only check ? */ return fixup_uint(param); } else { LM_CRIT("bug - too many params (%d) in lb_is_dst()\n",param_no); return -1; } }
static int w_lb_is_dst4(struct sip_msg *msg,char *ip,char *port,char *grp, char *active) { int ret, group; if (fixup_get_ivalue(msg, (gparam_p)grp, &group) != 0) { LM_ERR("Invalid lb group pseudo variable!\n"); return -1; } ret = lb_is_dst(*curr_data, msg, (pv_spec_t*)ip, (pv_spec_t*)port, group, (int)(long)active);
The return code of the script exported functions from the module are very important. 7. Adding module MI FunctionsAdding new module MI functions is done by populating the mi_cmds member in our module's exports structure.
The MI functions in the mi_cmds member of the exports structure will be automatically registered by the module interface. 8. Adding module StatisticsAdding new module exported statistics is done by populating the stats member in our module's exports structure.
The statistics in the stats member of the exports structure will be automatically registered by the module interface.
If our new module named mynewmod exports a statistic called mycustomstat we will be able to fetch that statistic by using opensipsctl : 9. Adding module Pseudo-variablesAdding new module pseudo-variables is done by populating the items member in our module's exports structure. 10. Adding module dedicated ProcessesFor certain use cases, our module might need to talk to external entities which are not SIP based. struct proc_export_ { char *name; /* name of the new task */ mod_proc_wrapper pre_fork_function; /* function to be run before the fork */ mod_proc_wrapper post_fork_function; /* function to be run after the fork */ mod_proc function; /* actual function that will be run in the context of the new process */ unsigned int no; /* number of processes that will be forked to run the above function */ unsigned int flags; /* flags for our new processes - only PROC_FLAG_INITCHILD makes sense here*/ }; typedef void (*mod_proc)(int no); typedef int (*mod_proc_wrapper)();
The function that will run in the context of the new process must never terminate. Once the function exits, the entire OpenSIPS will stop.
They are both executed within the context of the attendant OpenSIPS process
The number of processes forked by OpenSIPS for a particular function is not neccesarily static.
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 },
11. Module APIsWithin OpenSIPS, one module may sometimes need to access the functionality of another module (a common example are modules desiring to do operations on a per dialog basis, thus requiring part of the dialog module's functionality). Rather than directly accessing this functionality from within the target module, OpenSIPS uses the concept of a 'module exported API'. |