The RPC (Remote Procedure Call) interface is an interface for communicating with external applications. Using it an external application can call a function or procedure that will be executed inside SIP Server (SER or Kamailio). Function parameters are supported as well as returning multiple values as results.
By itself RPC consists of two APIs, one for defining RPC functions in a transport independent way (called the rpc module api) and one for implementing RPC transports.
The RPC transports are implemented by writting a RPC transport module. The most used transport modules are ctl , xmlrpc and jsonrpc-s .
ctl implements a proprietary fast and space efficient RPC encoding over different protocols (unix sockets, UDP, TCP, fifo).
xmlrpc uses the de-facto XML-RPC standard encoding (over HTTP TCP or TLS).
jsonrpc-s uses the de-facto JSON-RPC standard encoding (over HTTP TCP or TLS).
For more information about the existing transport modules, please refer to their documentation.
When writing a RPC procedure or function, one needs only use the RPC API and it will work automatically with all the transports and encodings. One needs only to load the desired RPC transport module (e.g. xmlrpc).
The RPC interface (or API) was created in such a way that would allow supporting XML-RPC (because XML-RPC is a de-facto standard), while in the same time being very easy to use.
Each module can export RPC functions just like it can export parameters and functions to be called from the script. Whenever SIP server receives an RPC request, it will search through the list of exported RPC functions and the function with matching name will be executed. A couple of essential RPC functions are also embedded into the SIP server core.
This section gives a detailed overview of the whole RPC API. Section 1.2.1, “RPC Functions” describes the prototype and conventions used in RPC functions. Section 1.2.2, “Data Types” gives a detailed overview of available data types that can be used in function parameters and return value. Section 1.2.3, “Getting Parameters” describes functions of the RPC API that can be used to retrieve parameters of the function, and finally Section 1.2.4, “Building Reply” describes functions of the API that can be used to build the result value that will be sent in the reply to the caller.
The whole RPC API is described in header file
sip_router/rpc.h
. This file defines the set
of functions that must be implemented by RPC transport modules, as
described in Section 1.4, “Implementing New Transports”, prototypes of RPC
functions and structures used for the communication between RPC
transport modules and ordinary modules exporting RPC functions.
RPC functions are standard C functions with the following prototype:
typedef void (*rpc_function_t)(rpc_t* rpc, void* ctx);
RPC functions take two parameters, first parameter is a pointer to rpc_t structure and the context. The rpc_t structure contains references to all API functions available to the RPC function as well as all data necessary to create the response. RPC functions do not return any value, instead the return value is created using functions from the context. The motivation for this decision is the fact that RPC functions should always return a response and even the API functions called from RPC functions should have the possibility to indicate an error (and should not rely on RPC functions doing so).
If no reply is sent explicitely, the RPC transport module will automatically send a "success" reply (e.g. 200 OK for XML-RPC) when the RPC function finishes. If no values are added to the response, the reponse will be an empty "success" reply (e.g. a 200 OK with empty body for XML-RPC). RPC API functions will automatically send an error reply upon a failure.
Each RPC function has associated an array of documentation strings. The purpose of the documentation strings is to give a short overview of the function, accepted parameters, and format of the reply. By convention the name of the documentation string array is same as the name of the function with "_doc" suffix.
Each module containing RPC functions has to export all the RPC functions to SIP server core in order to make them visible to the RPC transport modules. The export process involves a rpc_export_t structure (either by itself or in an array):
typedef struct rpc_export {
const char* name; /* Name of the RPC function (null terminated) */
rpc_function_t function; /* Pointer to the function */
const char** doc_str; /* Documentation strings, method signature and description */
unsigned int flags; /* Various flags, reserved for future use */
} rpc_export_t;
The flags
attribute of the
rpc_export
structure is reserved for future
use and is currently unused.
There are several ways of exporting the RPC functions to the SIP server core:
register a null terminated array of rpc_export_t structures
using the rpc_register_array()
function
(defined in rpc_lookup.h), from the module init function
(mod_init()). This is the recommended
method for all the new modules.
Example 1. usrloc RPC Exports Declaration
The rpc_export_t
array for the modules_s/usrloc
module looks like:
rpc_export_t ul_rpc[] = {
{"usrloc.statistics", rpc_stats, rpc_stats_doc, 0},
{"usrloc.delete_aor", rpc_delete_aor, rpc_delete_aor_doc, 0},
{"usrloc.delete_contact", rpc_delete_contact, rpc_delete_contact_doc, 0},
{"usrloc.dump", rpc_dump, rpc_dump_doc, 0},
{"usrloc.flush", rpc_flush, rpc_flush_doc, 0},
{"usrloc.add_contact", rpc_add_contact, rpc_add_contact_doc, 0},
{"usrloc.show_contacts", rpc_show_contacts, rpc_show_contacts_doc, 0},
{0, 0, 0, 0}
};
To register it from the module init function one would use something similar to:
if (rpc_register_array(ul_rpc) != 0) { ERR("failed to register RPC commands\n"); return -1; }
register RPCs one by one using the
rpc_register_function()
(defined in rpc_lookup.h), from the module init function.
register a null terminated array of rpc_export_t structures
using the SIP server module interface SER_MOD_INTERFACE (specific
for SER flavour).
For this purpose, the
module_exports
structure of SIP server module API
contains a new attribute called rpc_methods
:
struct module_exports {
char* name; /* null terminated module name */
cmd_export_t* cmds; /* null terminated array of the exported commands */
rpc_export_t* rpc_methods; /* null terminated array of exported rpc methods */
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 */
destroy_function destroy_f; /* function called upon shutdown */
onbreak_function onbreak_f;
child_init_function init_child_f; /* function called by all processes after the fork */
};
rpc_methods
is a pointer to an array of
rpc_export_t structures. The last element of the array is a
bumper containing zeroes in all the attributes of the
structure. The following program listing shows the exported RPC
functions of the modules_s/usrloc module, using the rpc_export_t array
ul_rpc defined above, in the
rpc_register_array() example:
Example 2. usrloc Module Exports Declaration
struct module_exports exports = {
"usrloc",
cmds, /* Exported functions */
ul_rpc, /* RPC methods */
params, /* Export parameters */
mod_init, /* Module initialization function */
0, /* Response function */
destroy, /* Destroy function */
0, /* OnCancel function */
child_init /* Child initialization function */ };
This mode works only with modules using the SER flavour module
interface. It does not work for kamailio modules and it
will probably not work for future sip-router modules. It is
safer and recommended to use instead the
rpc_register_array()
function.
By convention the name of every exported function consists of two parts delimited by a dot. The first part is the name of the module or SIP server subsystem this function belongs to. The second part is the name of the function.
The RPC API defines several basic and 1 compound data type
that can be used in communication with the caller of RPC
functions. The RPC API uses formating strings to describe data
types. Each data type is described by exactly one character in
the formating string. For example, if an RPC function calls
function add
of the RPC API and it passes
two parameters to it, the first one of type string and the
second one of type integer, the function parameters will look
like:
add("sd", string_param, int_param);
Character "s" in the formating string tells to the function that the 2nd parameter should be interpreted as string, character "d" in the formating string tells to the function that the 3rd parameter should be interpreted as signed integer.
Integer.
Integer type represents a signed 32-bit
integer. Corresponding character in the formating string is
"d". This parameter can be stored in C-style variable with
type int
.
Float.
Float type represents a signed floating point
number. Corresponding character in the formating string is
"f". Data of this type can be stored in C-style variables
of type double
.
String.
String type represents a string of characters. The string
may contain zeroes. This data type is represented by two
characters in the formatting string, either "s" or "S". "s"
indicates to the conversion function that the result should
be stored in a variable of type char*
and it should be zero terminated. "S" indicates to the
conversion function that the result will be stored in
a variable of type str
which contains
both the pointer to the beginning of the string and its
length.
Structure. Structure is the only compound data type currently defined in the API. A structure is a collection of attributes. Each attribute is identified using name (string) and each attribute can be one of the basic data types, that is integer, float, or string. Nesting of structures is not allowed (in other words, structure attributes cannot be of type struct again). Corresponding character in the formatting string is "{".
Optional parameters.
Optional parameters can be used, but only in the
scan
function. For optional parameters the
scan
function will not automatically generate
a rpc fault if the input ends. Note that in this case the
scan
will still return a negative value
(minus the number of parameters successfully read).
Optional parameters can be marked in the format string by
preceding the first optional parameter type with a "*".
All the parameters following a "*" are considered to be optional.
For example for the format string "ds*dds", the last 3 parameters
(2 ints and a string) are optional.
Table 1. Data Type Overview
Name | Formating String Char | C-Style Variable |
Integer | d | int |
Float | f | double |
String | s | char* |
String | S | str |
Optional modifier | * | marks all further parameters as optional |
Autoconvert modifier | . | requires auto-conversion for the next parameter |
Each RPC function call can contain parameters. Parameters have no name, their meaning is determined by their position in the parameter set.
You can pass all parameters to a function within a structure if you want to make them position independent. Then each parameter can be retrieved by its name regardless of its position.
There are two functions in the RPC API that can be used to
obtain function call parameters: scan
and
struct_scan
.
Function scan
can be used to retrieve
parameters from the parameter set. The function accepts
variable number of parameters. The first parameter is the
formatting string that determines the type of the
parameters to be retrieved. Each parameter is represented by
exactly one parameter type character in the string.
The variable part of parameters must contain as many pointers to C
variables as there are formatting non-modifiers characters in the
formatting string.
The function will crash if you fail to provide enough parameters.
Besides characters representing parameter types, the formatting string can contain two special modifiers: "*" and ".". The modifiers do not have a correspondent in the variable part of the parameters.
The meaning of "*" modifier is that any further parameters (defined by other type characters in the formatting string) are optional (they can be missing in the input and no rpc fault will automatically be generated).
The '.' modifiers turns on type autoconversion for the next parameter. This means that if the type of the next parameter differs from the type specified in the formatting string, the parameter will be automatically converted to the formatting string type (if possible) and if the automatic conversion succeeds, no fault will be generated.
The function returns the number of parameters read on success (a number greater or equal 0) and - (minus) the number of parameters read on error (for example for an error after reading 2 parameters it will return -2). When a failure occurs (incorrect parameter type or no more parameters in the parameter set) the function will return a negative number (- number of parameters read so far) and it will also automatically change the reply that will be sent to the caller to indicate that a failure has occurred on the server (unless the "*" is used and the error is lack of more parameters).
The prototype of the function is:
int scan((void* ctx, char* fmt, ...)
It is possible to either call the function once to scan all the parameters:
rpc->scan(ctx, "sdf", &string_val, &int_val, &double_val);
Or you can call the same function several times and it will continue where it left off previously:
rpc->scan(ctx, "s", &string_val); rpc->scan(ctx, "d", &int_val); rpc->scan(ctx, "f", &double_val);
Function struct_scan
can be used to
retrieve named attributes from a parameter of type
structure.
This function is obsolete and not implemented by all the
rpc transports (e.g.: ctl / binrpc). Consider using the normal
scan
instead.
When retrieving a structure parameter from the parameter set:
rpc->scan(ctx, "{", &handle);
The corresponding variable (named
handle
in the example above) will contain
the index of the structure parameter within the parameter
set, but the index cannot be used to retrieve the contents
of the structure. To retrieve the contents of the structure
you can use function struct_scan
. The
function gets the handle as the first parameter:
rpc->struct_scan(handle, "sd", "str_attr", &str_val, "int_attr", &int_val);
The second parameter is the formatting string followed by
pairs of parameters. First parameter in each pair is the
name of the attribute to retrieve (string) and the second
parameter in each pair is the pointer to the variable to
store the value of the parameter. The function returns the
number of parameters (name value pairs) read on
success and - number of parameters read so far on an error
(just like the scan
function). The function
also indicates an error if a requested attribute is missing in
the structure.
Example 3. Retrieving Parameters
static void rpc_delete_contact(rpc_t* rpc, void* ctx) { str aor, contact; char* table; void *handle; int expires; double q; if (rpc->scan(ctx, "sS{", &table, &aor, &handle) < 0) { /* Reply is set automatically by scan upon failure, * no need to do anything here */ return; } if (rpc->struct_scan(handle, "Sdf", "Contact", &contact, "Expires", &expires, "Q", &q ) < 0) { /* Reply is set automatically by struct_scan upon failure, * no need to do anything here */ return; } /* Process retrieved parameters here */ } /* variable number of parameters: echo back all the parameters, string type required */ static void core_prints(rpc_t* rpc, void* c) { char* string = 0; while((rpc->scan(c, "*s", &string)>0)) rpc->add(c, "s", string); } /* variable number of parameters and auto conversion: echo back all the parameters, works with any type (everything is internally converted to string, notice the '.' modifier) */ static void core_echo(rpc_t* rpc, void* c) { char* string = 0; while((rpc->scan(c, "*.s", &string)>0)) rpc->add(c, "s", string); }
The RPC API contains several functions that can be used to modify and/or send a reply. The functions use formatting strings and parameter lists just like functions described in Section 1.2.3, “Getting Parameters”.
Each RPC function call must return a reply. The reply can be either a failure reply or success reply. Failure replies contain only the status code and reason phrase. Success replies can have arbitrary amount of data attached to them. Status codes 3xx, 4xx, 5xx, and 6xx indicate failures. Status code 2xx indicates success.
The default reply is 200 OK with no data attached to it. This is what will be returned by the RPC transport module if you do not call any of the reply-related functions described in this section.
Example 4. Sending default reply
static void rpc_dummy(rpc_t* rpc, void *ctx) { /* 200 OK with no data will be returned */ }
You can use fault
function to indicate
that an error has occurred on the server to the caller. The
function accepts two parameters. The first parameter is the
status code and the second parameter is the reason phrase.
static void rpc_my_function(rpc_t* rpc, void *ctx) { rpc->fault(ctx, 600, "Not Yet Implemented"); }
If your function first creates some result using
add
, or printf
functions then all the data will be lost once you call
fault
function. Failure replies must
not contain any data:
static void rpc_my_function(rpc_t* rpc, void *ctx) { rpc->add(ctx, "s", "result1"); rpc->add(ctx, "d", variable); /* Reply created by previous functions will be * deleted and a failure reply 600 Not Yet Implemented * will be created instead */ rpc->fault(ctx, 600, "Not Yet Implemented"); /* You can also add data here, but that will have no * effect */ rpc->add(ctx, "s", "result2"); }
Similarly you can also call add
or
printf
functions after calling
fault
, in this case they will have no
effect:
static void rpc_my_function(rpc_t* rpc, void *ctx) { rpc->fault(ctx, 600, "Not Yet Implemented"); /* You can also add data here, but that will have no * effect and only 600 Not Yet Implemented will be returned */ rpc->add(ctx, "s", "result2"); }
RPC functions can use function send
to
explicitly send the reply. Each RPC function call generates
exactly one reply. No reply will be sent after the function
finishes if it already sent the reply using
send
function explicitly. This
function is especially useful if the RPC function needs to
perform some (potentially destructive) actions after the
reply has been sent.
Example 5. Kill the server
static void core_kill(rpc_t* rpc, void *ctx) { int sig_no; if (rpc->scan(ctx, "d", &sig_no) < 0) return; rpc->send(ctx, ); /* First send a reply */ kill(0, sig_no); /* Then kill the server */ }
Function add
can be used to add
arbitrary data to the result set. Its parameters and use
are analogical to scan
function
described in Section 1.2.3.1, “scan
”. The first
parameter of the function is the formatting string that
determines the types of additional parameters:
static void rpc_func(rpc_t* rpc, void *ctx) { str str_result; int int_result; void *handle; double float_result; if (rpc->add(ctx, "Sdf{", &str_result, int_result, float_result, &handle) < 0) return; }
Naturally you can call this function several times, adding only one piece of data at a time. The function returns 0 on success and -1 on an error. In case of an error the reply is set automatically with corresponding error code and reason phrase.
The last character in the formatting string of the function
above indicates that the last data to be added will be a
structure. This deserves some clarification. In this case,
the function will create an empty structure and the handle
to the newly created structure will be stored in
handle
variable (hence the last
parameter is pointer to an integer). In this particular
example parameters str_result
,
int_result
, and
float_result
will be used for reading
while parameter handle
will be used for
writing by the function.
You can set the attributes of the newly created structure
using struct_add
function described in
Section 1.2.4.5, “struct_add”.
rpl_printf
is a convenience function. The
function adds data of type string to the result set. The
first parameter of the function is again a formatting
string, but this time it is standard
printf
-like formatting string:
if (rpc->rpl_printf(ctx, "Unable to delete %d entries from table %s", num_entries, table_name) < 0) return;
The return value of the function is the same as of
add
function.
Function struct_add
can be used to add
attributes to a structure (created previously by
add
function). The first parameter of
the function is handle obtained through
add
function, the second parameters is
formatting string that determines the types of attributes
to be added. There must be two parameters per each
character in the formatting string, the first one is the
name of the attribute, the second parameter is the value
of the attribute. If a parameter with such a name already
exist in the structure then it will be overwritten with the
new value.
static void rpc_func(rpc_t* rpc, void *ctx) { void *handle; /* Create empty structure and obtain its handle */ if (rpc->add(ctx, "{", &handle) < 0) return; /* Fill-in the structure */ if (rpc->struct_add(handle, "sd", "attr1", str_val, "attr2", int_val ) < 0) return; }
The function returns -1 on an error (and sets the status code and reason phrase of the reply accordingly) and 0 on success.
The following example illustrates the use of most of the functions from the API together:
Example 6. Real World Example RPC Function
static void rpc_register(rpc_t* rpc, void *ctx) { char* domain; str aor; contact_t contact, new_contact; void *handle; /* Extract the domain, address of record from the request */ if (rpc->scan(ctx, "sS{", &domain, &aor, &handle) < 0) return; /* Extract the structure describing the contact to be processed */ if (rpc->struct_scan(handle, "Sdf", "Contact", &contact.c, "Expires", &contact.expires, "Q", &contact.q ) < 0) return; /* Process the contact, new_contact will contain updated value after processing */ if (process_contact(domain, &aor, &new_contact, &contact) < 0) { /* Processing failed, indicate the failure to the caller */ rpc->fault(ctx, 500, "Error While Processing Contact"); return; } /* Return the domain and the address of record */ rpc->add(ctx, "sS{", &domain, &aor, &handle) < 0) return; /* And also add the new values for contact, q, and expires parameters */ rpc->struct_add(handle, "Sdf", "Contact", &new_contact.c, "Expires", &new_contact.expires, "Q", &new_contact.q ); }
sercmd (C application that uses the binrpc interface implemented by the ctl module).
ser_ctl (python application that uses the XML-RPC interface implemented by the xmlrpc module).
serweb (php application that can use the XML-RPC interface to call ser functions).
See the xmlrpc
module documentation:
modules/xmlrpc/README.