$Revision$
$Date$
SER features modular architecture which allows us to split SER's
functionality across several modules. This approach gives us
greater flexibility, only required set of functions can be loaded
upon startup which minimizes the server's memory footprint. Modules
can be also provided by 3rd party developers and distributed
separately from the main server. Most of the functionality that SER
provides is available through modules, the core itself contains
only minimum set of functions that is essential for proper server's
behavior or that is needed by all modules.
This chapter provides detailed information on module interface of
SER, which is used to pass information on available functions and
parameters from the modules to the core.
Module Interface
Shared Objects
First it would be good to know how SER loads and uses modules
before we describe the module interface in detail. This section
gives a brief overview of SER's module subsystem.
SER modules are compiled as "shared objects". A file containing a
shared object has usually .so suffix. All modules (shared objects)
will be stored in one directory after installation. For example tm
module, which contains code essential for stateful processing, will
be stored in file named tm.so. By default
these files are stored in
/usr/local/lib/ser/modules directory.
You can later load the modules using loadmodule
command in your configuration file. If you want to load previously
mentioned tm.so module, you can do it using
loadmodule "/usr/local/lib/ser/modules/tm.so" in
your configuration file. This command invokes dynamic linker
provided by the operating system which opens
tm.so file, loads it into memory and resolves
all symbol dependencies (a module might require symbols from the
core, for example functions and variables).
As the last step of the module loading the core tries to find
variable named exports, which describes all
functions and parameters provided by the module. These functions
and parameters are later available to the server and can be used
either in the configuration file or by other modules.
Exporting Functions
Each module can provide zero or more functions, which can be
used in the configuration file or by other modules
internally. This section gives a detailed description of
structure describing exported functions and passing this
information to the core through the module interface.
Each function exported by a module must be described by
cmd_export_t structure. Structures
describing all exported functions are arranged into an array and
pointer to the array is then passed to the core. The last element
of the array must contain 0 in all its fields, this element serves
as the mark telling the core that this is the very last element and
it must stop scanning the array.
Each exported function is described by the following 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 */
int flags; /* Function flags */
};
typedef struct cmd_export_ cmd_export_t;
Meaning of the fileds:
char* name
This is the name under which the function will be visible
to the core. Usually it is the same as the name of the
corresponding function.
cmd_function function
cmd_function type is defined as follows:
typedef int (*cmd_function)(struct sip_msg*, char*, char*);
The first parameter is a SIP message
being processed, the other 2 parameters are given from the
configuration file.
From time to time you might need to export a function
that has different synopsis. This can happen if you
export functions that are supposed to be called by
other modules only and must not be called from the
configuration script. In this case you will have to do
type-casting otherwise the compiler will complain and
will not compile your module.
Simply put (cmd_function) just before the function
name, for example
(cmd_function)my_function. Don't
use this unless you know what are you doing ! The
server might crash if you pass wrong parameters to the
function later !
int param_no
Number of parameters of the function. It can be 0, 1 or
2. The function will be not visible from the configuration
script if you use another value.
fixup_function fixup
This is the function that will be used to "fixup" function
parameters. Set this field to 0 if you don't need this.
If you provide pointer to a fixup function in this field,
the fixup function will be called for each occurrence of
the exported function in the configuration script.
The fixup function can be used to perform some operation on
the function parameters. For example, if one of the
parameters is a regular expression, you can use the fixup
to compile the regular expression. The fixup functions are
called only once - upon the server startup and so the
regular expression will be compiled before the server
starts processing messages. When the server calls the
exported function to process a SIP message, the function
will be given the already compiled regular expression and
doesn't have to compile it again. This is a significant
performance improvement.
Fixup functions can also be used to convert string to
integer. As you have might noticed, the exported functions
accept up to 2 parameters of type char*. Because of that it
is not possible to pass integer parameters from the script
files directly. If you want to pass an integer as a
parameter, you must pass it as string (i.e. enclosed in
quotes).
Fixup function can be used to convert the string back to
integer. Such a conversion should happened only once because
the string parameter doesn't change when the server is
running. Fixup is therefore ideal place for the conversion,
it will be converted upon the server startup before the
server starts processing SIP messages. After the conversion
the function will get directly the converted value. See
existing modules for example of such a fixup function.
int flags
Usage of each function can be restricted. You may want to
write a function that can be used by other modules but
cannot be called from the script. If you write a function
that is supposed to process SIP requests only, you may want
to restrict it so it will be never called for SIP replies
and vice versa. That's what is flags field for.
This field is OR value of different flags. Currently only
REQUEST_ROUTE and REPLY_ROUTE flags are defined and used by
the core. If you use REQUEST_ROUTE flag, then the function
can be called from the main route block. If you use
REPLY_ROUTE flag, then the function can be called from
reply route blocks (More on this in the SER User's
Guide). If this field is set to 0, then the function can be
called internally (i.e. from other modules) only. If you
want to make your function callable anywhere in the script,
you can use REQUEST_ROUTE | REPLY_ROUTE.
Exporting Parameters
Each module can provide zero or more parameters, which can
affect the module's behavior. This section gives a detailed
description of structures describing exported parameters and
passing this information to the core through the module
interface.
Each parameter exported by a module must be described by
param_export_t structure. Structures
describing all exported parameters are arranged into an array and
pointer to the array is then passed to the core. The last element
of the array must contain 0 in all its fields, this element serves
as the mark telling the core that this is the very last element and
it must stop scanning the array (This is same as in array of
exported functions).
Each exported parameter is described by the following structure:
struct param_export_ {
char* name; /* null terminated param. name */
modparam_t type; /* param. type */
void* param_pointer; /* pointer to the param. memory location */
};
typedef struct param_export_ param_export_t;
Meaning of the fields:
char* name
This is null-terminated name of the parameters as it will
be used in the scripts. Usually this is the same as the
name of the variable holding the value.
modparam_t type
Type of the parameter. Currently only two types are
defined. PARAM_INT for integer parameters (corresponding
variable must be of type int), PARAM_STR for str parameters
(corresponding variable must be of type char*) and PARAM_STRING
for string parameters (corresponding variable must be of type char*).
void* param_pointer
Pointer to the corresponding variable (stored as void*
pointer, make sure that the variable has appropriate type
depending on the type of the parameter !).
Module Initialization
If you need to initialize your module before the server starts
processing SIP messages, you should provide
initialization function. Each module can provide two initialization
functions, main initialization function and child-specific
initialization function. Fields holding pointers to both
initialization functions are in main export structure (will be
described later). Simply pass 0 instead of function pointer if you
don't need one or both initialization functions.
The main initialization function will be called before any other
function exported by the module. The function will be called only
once, before the main process forks. This function is good for
initialization that is common for all the children (processes). The
function should return 0 if everything went OK and a negative error
code otherwise. Server will abort if the function returns a
negative value.
Per-child initialization function will be called
after the main process forks. The function
will be called for each child separately. The function should
perform initialization that is specific for each child. For example
each child process might open its own database connection to avoid
locking of a single connection shared by many processes. Such
connections can be opened in the per-child initialization
function. The function accepts one parameter which is rank
(integer) of child for which the function is being executed. This
allows developers to distinguish different children and perform
different initialization for each child. The meaning of return
value is same as in the main initialization function.
Module Clean-up
A module can also export a clean-up function that will be called by
the main process when the server shuts down. The function accepts
no parameters and return no value.
exports Structure - Assembling the Pieces Together
We have already described how a module can export functions and
parameters, but we haven't yet described how to pass this
information to the core. Each module must have variable named
exports which is structure module_exports. The
variable will be looked up by the core immediately after it loads
the module. The structure contains pointers to both arrays
(functions, parameters), pointers to both initialization functions,
destroy function and the callbacks. So the structure contains
everything the core will need.
The structure looks like the follows:
struct module_exports{
char* name; /* null terminated module name */
cmd_export_t* cmds; /* null terminated array of the exported commands */
param_export_t* params; /* null terminated array of the exported module parameters */
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 ser exit; can be null */
onbreak_function onbreak_f;
child_init_function init_child_f; /* function called by all processes after the fork */
};
Field description:
char* name
Null terminated name of the module
cmd_exports* cmds
Pointer to the array of exported functions
param_export_t* params
Pointer to the array of exported parameters
init_function init_f
Pointer to the module initialization function
response_function response_f
Pointer to function processing responses
destroy_function destroy_f
Pointer to the module clean-up function
onbreak_function onbreak_f
TBD
child_init_function init_child_f
Pointer to the per-child initialization function
Example - Simple Module Interface
Let's suppose that we are going to write a simple module. The
module will export two functions - foo_req
which will be processing SIP requests and
foo_int which is an internal function that can
be called by other modules only. Both functions will take 2
parameters.
/* Prototypes */
int foo_req(struct sip_msg* msg, char* param1, char* param2);
int foo_res(struct sip_msg* msg, char* param1, char* param2);
static cmd_export cmds[] = {
{"foo_req", foo_req, 2, 0, ROUTE_REQUEST},
{"foo_int", foo_int, 2, 0, 0 },
{0, 0, 0, 0}
};
The module will also have two parameters, foo_bar of type integer
and bar_foo of type string.
int foo_bar = 0;
char* bar_foo = "default value";
str bar_bar = STR_STATIC_INIT("default");
static param_export params[] = {
{"foo_bar", PARAM_INT, &foo_bar},
{"bar_foo", PARAM_STRING, &bar_foo},
{"bar_bar", PARAM_STR, &ar_bar.s},
{0, 0, 0}
};
We will also create both initialization functions and a clean-up function:
static int mod_init(void)
{
printf("foo module initializing\n");
}
static int child_init(int rank)
{
printf("child nr. %d initializing\n", rank);
return 0;
}
static void destroy(void)
{
printf("foo module cleaning up\n");
}
And finally we put everything into the exports structure:
struct module_exports exports = {
"foobar", /* Module name */
cmds, /* Exported functions */
params, /* Exported parameters */
mod_init, /* Module initialization function */
0, /* Response function */
destroy, /* Clean-up function */
0, /* On Cancel function */
child_init /* Per-child init function */
};
And that's it.
Module Interface Internals
All the data structures and functions mentioned in this section can
be found in files sr_module.h and
sr_module.c.
Module Loading
Modules are compiled and stored as shared objects. Shared
objects have usually appendix ".so". Shared objects can be
loaded at runtime.
When you instruct the server to load a module using
loadmodule command in the config file, it
will call function load_module. The
function will do the following:
It will try to open specified file using
dlopen. For example if you
write loadmodule "/usr/lib/ser/modules/auth.so" in
the config file, the server will try to open file
"/usr/lib/ser/modules/auth.so" using
dlopen function.
If dlopen failed, the server
will issue an error and abort.
As the next step, list of all previously loaded
modules will be searched for the same module. If
such module is found, it means, that user is trying
to load the same module twice. In such case a
warning will be issued and server will abort.
The server will try to find pointer to "exports"
symbol using dlsym in the
module. If that fails, server will issue an error
and abort.
And as the last step, function
register_module will register
the module with the server core and loading of the
module is complete.
Function register_module registers a
module with the server core. By registration we mean the
following set of steps (see function
register_module in file
sr_module.c for more details):
The function creates and initializes new instance
of sr_module structure.
path field will be set
to path of the module.
handle field will be set
to handle previously returned by
dlopen.
exports field will be
set to pointer to module's
exports structure previously
obtained through dlsym in
load_module function.
As the last step, the newly created structure will
be inserted into linked list of all loaded modules
and registration is complete.
Module Configuration
In addition to set of functions each module can export set of
configuration variables. Value of a module's configuration
variable can be changed in the config file using modparam function. Module
configuration will be described in this section.
Function modparam
modparam function accepts three
parameters:
module name - Name of
module as exported in
name field of
exports global variable.
variable name - Name of
variable to be set - it must be one of names
specified in
param_names field of
exports variable of the
module.
value - New value of the
variable. There are two types of variables:
string and integer. If the last parameter
(value) of modparam
function is enclosed in quotes, it is string
parameter and server will try to find the
corresponding variable among string parameters
only.
Otherwise it is integer parameter and server
will try to find corresponding variable among
integer parameters only.
Function set_mod_param
When the server finds modparam
function in the config file, it will call
set_mod_param function. The function
can be found in modparam.c file. The
function will do the following:
It tries to find corresponding variable using
find_param_export function.
If it is string parameter, a new copy of the string
will be obtained using strdup
function and pointer to the copy will be stored in
the variable.
If it is integer parameter, its value will be
simply copied in the variable.
Function find_param_export
This function accepts 3 parameters:
module - Name of module.
parameter - Name of parameter to be found.
type - Type of the parameter.
The function will search list of all modules until it finds
module with given name. Then it will search through all
module's exported parameters until it finds parameter with
corresponding name and type. If such parameter was found,
pointer to variable holding the parameter's value will be
returned. If the function failed to find either module or
parameter with given name and type then zero will be
returned.
Looking Up an Exported Function
If you need to find exported function with given name and
number of parameters, find_export function
is what you need. The function is defined in
sr_module.c file. The function accepts
two parameters:
name - Name of function to be
found.
param_no - Number of
parameters of the function.
The function will search through list of all loaded modules and
in each module through array of all exported functions until it
finds function with given name and number of parameters. If
such exported function was found,
find_exported will return pointer to the
function, otherwise zero will be returned.
Additional Functions
There are several additional functions defined in file
sr_module.c. There functions are mostly
internal and shouldn't be used directly by user. We will
shortly describe them here.
register_builtin_modules - Some
modules might be linked statically with main
executable, this is handy for debugging. This function
will register all such modules upon server startup.
init_child - This function will
call child-initialization function of all loaded
modules. The function will be called by the server core
immediately after the fork.
find_module - The function accepts
pointer to an exported function and number of
parameters as parameters and returns pointer to
corresponding module that exported the function.
destroy_modules - The function
will call destroy function of all loaded modules. This
function will be called by the server core upon shut
down.
init_modules - The function will
call initialization function of all loaded modules. The
function will be called by the server before the fork.