|
Documentation |
Documentation.Development-Manual-Module-Dev-3-5 HistoryHide minor edits - Show changes to markup December 11, 2019, at 07:20 PM
by -
Added lines 456-463:
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'. December 11, 2019, at 07:11 PM
by -
Added lines 1-455:
Documentation -> Development Manual 3.5? -> Module DevelopmentThis page has been visited 819 times. (:title OpenSIPS Development - Module Development:) (:allVersions Development-Manual-Module-Dev 3.5:)
(:toc-float Table of Content:) 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. (:source lang=C -link -getcode :) loadmodule "mynewmod.so" (:sourceend:)
(:source lang=C -link -getcode :) 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 */
};
(:sourceend:)
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 */
};
(:sourceend:) Compiling a moduleFurther on, we will be following the various options we have in building our new module, named ournewmod.
(:source lang=C -link -getcode :)
include ../../Makefile.defs auto_gen= NAME=ournewmod.so LIBS= include ../../Makefile.modules
(:sourceend:)
include ../../Makefile.modules
(:sourceend:)
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. Initializing the moduleIn the context of initializing our new module, there are two types of functions that will help us : mod_initThis function must be specified in the init_f member of our module_exports exports structure. (:source lang=C -link -getcode :)
/* MUST return 0 in case of success, anything else in case of error */
typedef int (*init_function)(void);
(:sourceend:)
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. child_initThis function must be specified in the init_child_f member of our module_exports exports structure. (:source lang=C -link -getcode :)
/* MUST return 0 in case of success, anything else in case of error */
typedef int (*child_init_function)(int rank);
(:sourceend:)
(:source lang=C -link -getcode :)
(:sourceend:)
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.
Destroying the moduleThis function must be specified in the destroy_function member of our module_exports exports structure. (:source lang=C -link -getcode :) typedef void (*destroy_function)(); (:sourceend:) 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 : (:source lang=C -link -getcode :) struct param_export_ { char* name; /*!< null terminated param. name */
modparam_t type; /*!< param. type */
void* param_pointer; /*!< pointer to the param. memory location */
};
(:sourceend:)
(:source lang=C -link -getcode :) 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 }
}
(:sourceend:)
modparam("ournewmod","enable_stats", 1)
modparam("ournewmod","db_url","mysql://vlad:mypw@localhost/opensips")
(:sourceend:)
(:source lang=C -link -getcode :) 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 */
} (:sourceend:) Adding module FunctionsAdding new module parameters is done by populating the cmds member in our module's exports structure. 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 */
};
(:sourceend:)
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 :
(:source lang=C -link -getcode :)
typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*);
(:sourceend:)
The flags member in the cmd_export_ structure dictates where within the OpenSIPS script can that particular function be called. Current options here are : (:source lang=C -link -getcode :)
(:sourceend:)
{"lb_is_destination",(cmd_function)w_lb_is_dst4, 4, fixup_is_dst,
0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
(:sourceend:)
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;
}
}
(:sourceend:)
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);
(:sourceend:)
The return code of the script exported functions from the module are very important. 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. 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 : Adding module Pseudo-variablesAdding new module pseudo-variables is done by populating the items member in our module's exports structure. Adding module dedicated ProcessesFor certain use cases, our module might need to talk to external entities which are not SIP based. 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)(); (:sourceend:)
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.
{"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 },
(:sourceend:)
|
