OpenSIPS Event Interface – Part 1



External applications can interact with OpenSIPS through Management Interface (MI) which is pull-based mechanism and Event Interface which is push-based mechanism. The event interface acts as a mediator between OpenSIPS regular modules, the configuration script and the transport modules. The events can be published by the modules and the configuration file/script. The external applications subscribes for a certain event and OpenSIPS event interface notifies the external application when that event is raised. OpenSIPS has some hardcoded events such as E_PIKE_BLOCKED which is raised by the “Pike” module), E_CORE_PKG_THRESHOLD which is raised when private memory usage exceeded a threshold,…..etc. Several transport protocols are used to send the event notification to the external subscribed application. Each protocol is provided by separate OpenSIPS module.

Events Raised from OpenSIPS Modules

If the module wants to export events, it must register them in its initialization function (init_function” member in the module interface “struct module_exports”). The function “evi_publish_event” defined in “evi/evi_modules.h” is used to register an event. The function “evi_raise_event” is used to raise an event. The function “evi_probe_event” checks if there is any subscriber for that event. This function is called before building parameters list because if no subscriber has been found, then the delay of building parameters list and other things can be skipped.

event_id_t evi_publish_event(str event_name);

int evi_raise_event(event_id_t id, evi_params_t* params);

int evi_probe_event(event_id_t id);

Events Raised from OpenSIPS Script

To raise an event from script, you have to call the core function “raise_event” from the script. The first parameter of this function is the event name and it is constant string parameter (pseudo variable cannot be used) because the event must be registered at startup and the pseudo variable would have an undefined value. The second and third parameters form together the list of the parameters and they are optional. This is how to call this function:

$avp(attr-name) = “param1”;

$avp(attr-name) = “param2”;

$avp(attr-val) = 1;

$avp(attr-val) = “2”

raise_event(“E_Example”, $avp(attr-name), $avp(attr-val));

Click here to see the full explanation.

The length of both AVPs have the same number of parameters.

Transport Module Interface

This interface is implemented by all transport modules that are used to send the event notification to the external applications. It is defined in the file “evi/evi_transport.h” as following:

typedef struct evi_export_ {
str proto;                      /* protocol name / transport module name */
raise_f *raise;         /* raise function */
parse_f *parse;         /* parse function */
match_f *match;         /* sockets match function */
free_f *free;           /* free a socket */
print_f *print;         /* prints a socket */
unsigned int flags;
} evi_export_t;

The first field is the name of the transport protocol. It is used by the MI subscription command so the event notification will be sent by the transport named in the subscription request. The second filed is the raise function which will be called by the OpenSIPS Event Interface for each subscriber, to send the notification on this transport. This function serializes the event parameters, and send them to the subscriber. The event parameters are passed to the transport module from the module that raised the event.

The third field is the parse function which parses the subscriber socket. The match function is used to update the expire period of the subscription or unsubscribe the application. The rest of fields are easy to understand.

The transport module which implement this interface must be registered to OpenSIPS event interface. This is can be done by calling the function with the an instance of the above structure as a parameter:

int register_event_mod(evi_export_t *ev);

The Availability of the Events

All exported events will be available at startup time after the parsing of the configuration file is completed. The external application can only subscribe to en event after OpenSIPS has been started up.

Event Subscription

To subscribe for events, the external application send this MI subscription command:

event_subscribe Event_Name Transport Socket Expire

Example: #opensipsctl fifo event_subscribe E_PIKE_BLOCKED udp: 1200

If the subscription succeeded, the application gets 200 OK message and it will start getting notifications for the subscribed event when the event is raised. The external application suppose to do something upon receiving the notification. For example: update the firewall, do some processing, or just printing or send information mail to administrator.

More Information



I/O Design Patterns For SIP Routers



In real-time applications like SIP applications, we have a lot of IO operations (database IO operations, network IO operations,…etc.) and the time to accomplish each IO operation is critical. Using event based architecture with asynchronous IO minimizes the time of IO operation and scale up the performance.

Here i just would like to explain the concept of reactor and proactor patterns for I/O operations. Both the reactor and proacor depend on event firing but the difference who is doing the actual IO operations.

Reactor Pattern

The reactor pattern involves synchronous I/O. The event here is defined as “ready to read or write”. When this kind of IO event is fired, the event demultiplexer passes the event to the registered user event handler/callback. The user event handler/callback function will be called to do the actual IO operation (read or write).

Proactor Pattern

The proactor IO pattern involves asynchronous IO. The event here is defined as “read or write is completed“. When this kind of IO event is fired, the user callback function/event handler will be called to process the data if the operation was “read” and do something else (new async operation) if the operation was “write”. Here the actual IO operation is done by one of the kernel threads which does the actual IO operation using a user defined buffer passed to it when the asynchronous system call is done. When the kernel finished the IO operation, it notifies the event demultiplexer and this will pass the event to the user handler/callback. As you can see the operating system must support the asynchronous IO so it can use the user buffer to do the actual IO operation to save the user application time.