Documentation

Documentation.Development-Manual History

Show minor edits - Show changes to markup

October 15, 2022, at 04:50 AM by NormB -
Changed line 654 from:

Further on we will follow the implementation of the $ru pseudovariable, which offers read/write access to the SIP message Request-URI.

to:

Further on we will follow the implementation of the $ru pseudo variable, which offers read/write access to the SIP message Request-URI.

Changed line 668 from:

Since the $ru pvar does not support indexing by concept ( the SIP message has one and only one Request-URI ), there is no need to add a parsing or an indexing function for our pseudovariable. Also, no special initialization is needed for our psedovariable case, since we will operate directly on the currently processed SIP message in the script.

to:

Since the $ru pvar does not support indexing by concept ( the SIP message has one and only one Request-URI ), there is no need to add a parsing or an indexing function for our pseudo variable. Also, no special initialization is needed for our pseudo variable case, since we will operate directly on the currently processed SIP message in the script.

Changed line 686 from:
      if(msg->first_line.type == SIP_REPLY)   /* REPLY doesnt have a ruri */
to:
      if(msg->first_line.type == SIP_REPLY)   /* REPLY doesn't have a ruri */
Changed lines 825-826 from:

OpenSIPS has its own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the use case and the menuconfig-provided compilation flags, the OpenSIPS generic locks can be switched to:

to:

OpenSIPS has its own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the use case and the menuconfig provided compilation flags, the OpenSIPS generic locks can be switched to:

Changed line 1068 from:

This can prove very useful when having an use case where all the OpenSIPS processes need read-only access to a resource, but you need to have an MI command to reload that resource (e.g. from a database). In such scenarios, using a readers-writers lock can improve performance by a considerable margin. \\

to:

This can prove very useful when having a use case where all the OpenSIPS processes need read-only access to a resource, but you need to have an MI command to reload that resource (e.g. from a database). In such scenarios, using a readers-writers lock can improve performance by a considerable margin. \\

Changed line 1290 from:
         - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour (e.g: MI functions that send out SIP messages and do not wait for their reply)
to:
         - MI_ASYNC_RPL_FLAG - the function has an asynchronous behavior (e.g: MI functions that send out SIP messages and do not wait for their reply)
Changed line 1698 from:
 * host:     Hosname or IP address of the host where database server lives (mandatory)
to:
 * host:     Hostname or IP address of the host where database server lives (mandatory)
Changed line 1720 from:

Connection sharing between multiple processes does not work for the majority of back-end specific connectors ( eg. MySQL, Postgres, etc ). Due to this fact, the developers MUST make sure to create a sepparate database connection for each process that will eventually need one - in the context of Module development, the connections need to be opened in the child_init function.

to:

Connection sharing between multiple processes does not work for the majority of back-end specific connectors ( eg. MySQL, Postgres, etc ). Due to this fact, the developers MUST make sure to create a separate database connection for each process that will eventually need one - in the context of Module development, the connections need to be opened in the child_init function.

Changed line 1783 from:
 * _r will point to a dynamically allocated structure, it is neccessary to call
to:
 * _r will point to a dynamically allocated structure, it is necessary to call
Changed line 1817 from:
 * Note: A variable of type db_res_t returned by db_query function uses dynamicaly
to:
 * Note: A variable of type db_res_t returned by db_query function uses dynamically
Changed line 1849 from:
 * recognized and converted by the database API. These datatypes are automaticaly
to:
 * recognized and converted by the database API. These datatypes are automatically
Changed lines 2336-2337 from:
                                           processes reqired by the module */
to:
                                           processes required by the module */
Changed lines 2348-2349 from:

The module_exports contents ( along with the above coments ) are self-explanatory.

to:

The module_exports contents ( along with the above comments ) are self-explanatory.

Changed lines 2649-2651 from:

As we can see, the input char* parameters are casted to their according valus after fixup.
Again, mod_fix.h provides varius functions for accessing the results of the fixup.In our example, fixup_get_ivalue is used to get the provided integer value ( either directly from the plaintext provided integer, or it will extract the integer value from the spec that was parsed at fixup ). Also, note how the active parameter is cast directly to long, since we're accepting only plain-text integers for that parameter, and it has been already converted for us at fixup time.

to:

As we can see, the input char* parameters are casted to their according values after fixup.
Again, mod_fix.h provides various functions for accessing the results of the fixup.In our example, fixup_get_ivalue is used to get the provided integer value ( either directly from the plaintext provided integer, or it will extract the integer value from the spec that was parsed at fixup ). Also, note how the active parameter is cast directly to long, since we're accepting only plain-text integers for that parameter, and it has been already converted for us at fixup time.

Changed line 2654 from:

A strictly positive return code will mean success, while a stricly negative return code will signal a failure \\

to:

A strictly positive return code will mean success, while a strictly negative return code will signal a failure \\

Changed line 2725 from:

The number of processes forked by OpenSIPS for a particular function is not neccesarily static.

to:

The number of processes forked by OpenSIPS for a particular function is not necessarily static.

Changed lines 2747-2748 from:

The common approach used throughout OpenSIPS is that the target module should implement a form of loading it's API - which in fact translates into populating a structure with pointers of the functions that need to be exported, as well as various other structure members that indicate various behaviour.
The module that needs to operate with the above API should first call ( within it's mod_init ) the function to bind to the needed module's API, and then operate with the received structure. \\

to:

The common approach used throughout OpenSIPS is that the target module should implement a form of loading it's API - which in fact translates into populating a structure with pointers of the functions that need to be exported, as well as various other structure members that indicate various behavior.
The module that needs to operate with the above API should first call ( within its mod_init ) the function to bind to the needed module's API, and then operate with the received structure. \\

Changed line 2864 from:
       serached.
to:
       searched.
Changed lines 2875-2876 from:
   succesfully be performed for the local address.
to:
   successfully be performed for the local address.
Changed line 2935 from:
    The function will return 0 in case of success ( request is succesfully routed ). Otherwise,
to:
    The function will return 0 in case of success ( request is successfully routed ). Otherwise,
Changed line 3058 from:
                        * DLGCB_CREATED - callback will get called when a new dialog is created. The callback will be called when the dialog is fully initialized ( create_dialog() was called either from script or from the API and the Transaction associated to the initial invite is fully initialised as well ). This callback must be registered alone, and the dlg_cell* provided at registration time must be NULL ( since it's a global cb type, not associated to any particular dialog )
to:
                        * DLGCB_CREATED - callback will get called when a new dialog is created. The callback will be called when the dialog is fully initialized ( create_dialog() was called either from script or from the API and the Transaction associated to the initial invite is fully initialized as well ). This callback must be registered alone, and the dlg_cell* provided at registration time must be NULL ( since it's a global cb type, not associated to any particular dialog )
Changed line 3063 from:
                        * DLGCB_EXPIRED - callback will get called when a dialog lives past it's assigned timeout ( see the $DLG_timeout script pvar ). This is a per dialog callback, so the first dlg parameter MUST be provided.
to:
                        * DLGCB_EXPIRED - callback will get called when a dialog lives past its assigned timeout ( see the $DLG_timeout script pvar ). This is a per dialog callback, so the first dlg parameter MUST be provided.
Changed line 3067 from:
                        * DLGCB_MI_CONTEXT - callback will get called when the 'dlg_list_ctx' MI function is called. Useful when modules binded to the dialog module API want to append nodes to the dlg_list_ctx MI response tree. This is a per dialog callback, so the first dlg parameter MUST be provided.
to:
                        * DLGCB_MI_CONTEXT - callback will get called when the 'dlg_list_ctx' MI function is called. Useful when modules bind to the dialog module API want to append nodes to the dlg_list_ctx MI response tree. This is a per dialog callback, so the first dlg parameter MUST be provided.
Changed line 3097 from:
        Returns the current dialog pointer. In case of no created dialog or other internall errors, NULL is returned.
to:
        Returns the current dialog pointer. In case of no created dialog or other internal errors, NULL is returned.
Changed lines 3106-3107 from:
                profiles - the NULL terminated string containing one or multiple profile definitions ( sepparated by ';' )
                has_value - whether our profies will contain values with counters, or they will be just stand-alone counters
to:
                profiles - the NULL terminated string containing one or multiple profile definitions ( separated by ';' )
                has_value - whether our profiles will contain values with counters, or they will be just stand-alone counters
October 15, 2022, at 12:20 AM by NormB -
Changed line 2305 from:

An OpenSIPS module is actually a shared library ( .so file ) which OpenSIPS can dinamically load at OpenSIPS startup, if the module is loaded from within the OpenSIPS script, by using the loadmodule directive :

to:

An OpenSIPS module is actually a shared library ( .so file ) which OpenSIPS can dynamically load at OpenSIPS startup, if the module is loaded from within the OpenSIPS script, by using the loadmodule directive :

November 22, 2019, at 04:25 PM by liviu -
Changed line 1271 from:

The Management Interface is the abstract layer that is commonly used to control and monitor OpenSIPS. The MI Interface supports multiple actual back-ends ( eg. FIFO, Datagram, XMLRPC, HTTP GET JSON, etc ) - due to the modularity of the interface and also due to the clear separation between the logic and the transport layer, the developer just defines the functions to be externally called, and then it is up to the OpenSIPS script writer to chose what transport he will actually use for controlling OpenSIPS.

to:

The Management Interface is the abstract layer that is commonly used to control and monitor OpenSIPS. The MI Interface supports multiple actual back-ends (e.g. FIFO, Datagram, JSON-RPC, XML-RPC). Thanks to its modularity and clear separation between structuring of the data (logic) and representation of the data structures (transport) layers, the developer is only left to define the functions which (de)structure the data, and then it is up to the OpenSIPS script writer to choose what transport she will actually use for controlling OpenSIPS.

Changed lines 1273-1275 from:

The MI interface heavily uses trees :

  • the Interface will provide as input a tree with the parameters provided by the user
  • an MI function has to also return a tree, which will then be converted by the transport module to it's specific representation
to:

The MI interface heavily uses JSON in order to interface with its transport layer:

  • the interface will provide as input a JSON (mi_item_t) to the user-defined MI function, with its required input fields
  • an MI function must also return a JSON, which will then be converted by the transport layer to the appropriate representation
Changed line 1277 from:

Further on we will focus on the core MI functions, with a specific focus on the debug function. Note that modules can ( an commonly ) also export MI functions - see the Modules Development MI functions topic for more information on that.

to:

Further on, we will focus on the core MI functions, with a specific focus on the log_level MI function. Note that modules may also export MI functions (and commonly do so) - see the Modules Development MI functions topic for more information on that.

Changed lines 1283-1299 from:
        /* the name of the function ( users will call this from their transport of choice */
        char *name;
        /* short description of the usage of this function */
        char *help;
        /* actual function that will get called */
        mi_cmd_f *cmd;
        /* flags for this function. Currently options are :
             - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour ( eg: MI functions that send SIP messages and wait for their reply )
             - MI_NO_INPUT_FLAG - the function does not receive any parameters 
        */
        unsigned int flags;
        /* parameter that will be passed when the cmd function gets called */
        void *param;
        /* the initialization function to be called by OpenSIPS ( one time ) */
        mi_child_init_f *init_f;

}mi_export_t;

to:
    /* the name of the function users will invoke it from their transport of choice */
    char *name;

    /* short description of the usage of this function */
    char *help;

    /* flags for this function.  The current options are :
         - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour (e.g: MI functions that send out SIP messages and do not wait for their reply)
         - MI_NO_INPUT_FLAG - the function does not receive any parameters
    */
    unsigned int flags;

    /* an initialization function to be called by OpenSIPS (one time) */
    mi_child_init_f *init_f;

    /* the possible combinations of arguments which may be supplied to this function, along with their handlers */
    mi_recipe_t recipes[MAX_MI_RECIPES];

} mi_export_t;

Deleted lines 1303-1309:
        { "uptime", "prints various time information about OpenSIPS - "
                "when it started to run, for how long it runs",
                mi_uptime,     MI_NO_INPUT_FLAG,  0,  init_mi_uptime },
        { "version", "prints the version string of a runningOpenSIPS",
                mi_version,    MI_NO_INPUT_FLAG,  0,  0 },
        { "pwd", "prints the working directory of OpenSIPS",
                mi_pwd,        MI_NO_INPUT_FLAG,  0,  0 },
Added lines 1305-1312:
    { "log_level", "gets/sets the per process or global log level in OpenSIPS",
        0, 0, {
        {w_log_level,   {0}},
        {w_log_level_1, {"level", 0}},
        {w_log_level_2, {"level", "pid", 0}},
        {EMPTY_MI_RECIPE}
        }
    },
Changed lines 1314-1316 from:

...

to:

};

Changed line 1327 from:

if (register_mi_mod( "core", mi_core_cmds)<0) {

to:

if (register_mi_mod( "core", mi_core_cmds) < 0) {

Changed line 1329 from:
      return -1;  
to:
      return -1;
Changed line 1333 from:

The structures commonly used for implementing MI functions are also found in mi/mi.h :

to:

The structures commonly used for implementing MI functions can be found in mi/mi.h and mi/item.h :

Changed lines 1336-1340 from:

Parameters :

      input : the tree that contains the command paramenters
      param : the parameter provided at function registration

Returns :

      A mi_root tree containing the function reply
to:

Parameters:

      params : the JSON tree which includes the input arguments
      async_hdl : if the function has async capabilities, this is its async handler

Returns:

      An mi_response_t JSON structure with the requested data or execution result
Changed lines 1343-1371 from:

typedef struct mi_root* (mi_cmd_f)(struct mi_root *input, void *param);

/* below are the used structures for representing the tree root and the tree nodes */ struct mi_root {

      /* int code - similar to SIP or HTTP code */
      unsigned int       code;
      /* string reason for code - similar to SIP or HTTP reason */
      str                reason;
      /* handler in case of asynchronous MI commands */ 
      struct mi_handler  *async_hdl;
      /* the actual root node in our tree */
      struct mi_node     node; 

};

struct mi_node {

      str value;
      str name;
      unsigned int flags;
      struct mi_node *kids;
      struct mi_node *next;
      struct mi_node *last; 
      struct mi_attr *attributes;

};

struct mi_attr{

        str name;
        str value;
        struct mi_attr *next;

};

to:

typedef mi_response_t *(mi_cmd_f)(const mi_params_t *params,

                                        struct mi_handler *async_hdl);

/* Both mi_item_t and mi_response_t point to the same cJSON struct, see lib/cJSON.h */ typedef struct cJSON {

    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
    char *valuestring;
    /* The item's number, if type==cJSON_Number */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;

} cJSON;

Changed lines 1370-1372 from:

As can be noted from the above tree definition, a node has a name and a value associated to it,it can have one or multiple children ( stored in kids pointer ), and also it can have a list of key-value attributes associated to it.
For building the output MI tree, mi/tree.h and mi/attr.h expose the following functions :

to:

For building the output JSON, mi/item.h exposes the following functions:

November 22, 2019, at 02:53 PM by liviu -
Changed lines 1178-1179 from:

The OpenSIPS timer architecture consists of the following processes, which may be listed by running opensips-cli -x mi ps:

to:

The OpenSIPS timer architecture involves the following types of processes (which may be listed at runtime by running opensips-cli -x mi ps):

Changed lines 1182-1184 from:
  • "SIP receiver" and "TCP receiver" - SIP workers, responsible for SIP message processing. Yet any of them may also receive an above-mentioned high-priority timer job sent by the "timer" worker, which they will immediately execute, despite any pending SIP messages on the network
  • "Timer handler" - for the cases when the SIP workers are so busy processing traffic (e.g. they are all stuck doing some slow DB queries) that timer jobs are at risk of getting delayed, this process is meant to save the day and ensure the timer jobs execute on time
to:
  • "SIP receiver" and "TCP receiver" - SIP workers, responsible for SIP message processing. Yet any of them may also receive an above-mentioned high-priority timer job sent by the "timer" worker, which they will immediately execute, disregarding any pending SIP messages on the network
  • "Timer handler" - for the cases where the SIP workers are so busy processing traffic (e.g. they are all stuck in some slow DB queries) that timer jobs are at risk of getting delayed, this process is meant to save the day and ensure the timer jobs execute on time
Changed line 1254 from:

Also, the OpenSIPS API also exposes the number of seconds and microseconds passed from the OpenSIPS start time. These can be accessed by calling

to:

Additionally, the timer.h API exposes the time elapsed since OpenSIPS startup:

Changed line 1258 from:
      the number of seconds elapsed from OpenSIPS start
to:
      the number of seconds elapsed since OpenSIPS start
Changed line 1264 from:
      the number of microseconds elapsed from OpenSIPS start
to:
      the number of microseconds elapsed since OpenSIPS start
November 22, 2019, at 02:47 PM by liviu -
Changed lines 1177-1183 from:

OpenSIPS exposes it's own API for implementing timer functions, with seconds and microsecond precision.
The OpenSIPS timer architecture is made out of the so called 'timer keeper' , which is a process which just increments global counter of second and microsecond precision, and one process which will execute the various timer functions when their time to execute has arrived.
Also, the OpenSIPS timers support automatic re-calibration in order to compensate timer drifting in case the timer executed functions do not complete in timely manners.

Global Timer Process

timer.h exposes all the relevant functionalities for operating the OpenSIPS timers. For registering a new timer function with second precision, use :

to:

OpenSIPS exposes an API for recurrent job scheduling, with second and microsecond precision.
The OpenSIPS timer architecture consists of the following processes, which may be listed by running opensips-cli -x mi ps:

  • "time_keeper" - a process which keeps track of the time since startup, using two global counters of second and microsecond precision. It also
  • "timer" - a process responsible for timer job scheduling. It does not run the jobs themselves, it simply dispatches them for execution to other processes
  • "SIP receiver" and "TCP receiver" - SIP workers, responsible for SIP message processing. Yet any of them may also receive an above-mentioned high-priority timer job sent by the "timer" worker, which they will immediately execute, despite any pending SIP messages on the network
  • "Timer handler" - for the cases when the SIP workers are so busy processing traffic (e.g. they are all stuck doing some slow DB queries) that timer jobs are at risk of getting delayed, this process is meant to save the day and ensure the timer jobs execute on time

timer.h exposes all the relevant functionality for scheduling recurring jobs in OpenSIPS. To register a new timer function with second precision, use:

Changed lines 1188-1189 from:

Parameters :

	label – opaque string containing a short timer function description ( to be used for logging )
to:

Parameters:

	label – opaque string containing a short function description (for displaying/logging purposes)
Changed lines 1193-1195 from:

Returns :

to:
	flags – the nature of the job: is it real-time (must be skipped if it cannot run on time) or critical (must never skip a single run, despite delays)

Returns:

Changed line 1199 from:
      unsigned int interval);
to:
      unsigned int interval, unsigned short flags);
Changed lines 1201-1204 from:

The seconds callback Parameters :

      ticks - represents the current number of seconds since OpenSIPS startup when the callback is called at
      param - is the parameter provided at timer function registration.
to:

The timer job prototype:

Parameters:

      ticks – represents the current number of seconds since OpenSIPS startup
      param - the parameter provided at job registration
Changed line 1207 from:

typedef void (timer_function)(unsigned int ticks, void* param);

to:

typedef void (timer_function)(unsigned int ticks, void* param);

Changed line 1210 from:

For registering a microsecond timer, you should use

to:

To register a microsecond-precision job, you can use:

Changed lines 1213-1214 from:

Parameters :

	label – opaque string containing a short timer function description ( to be used for logging )
to:

Parameters:

	label – opaque string containing a short function description (for displaying/logging purposes)
Changed lines 1218-1219 from:

Returns :

	0 in case of success, negative code in case of internal error.
to:
	flags – the nature of the job: is it real-time (must be skipped if it cannot run on time) or critical (must never skip a single run, despite delays)

Returns:

	0 in case of success, negative code in case of internal error
Changed lines 1223-1224 from:

int register_utimer(char *label, utimer_function f, void* param, unsigned int interval);

to:

int register_utimer(char *label, utimer_function f, void* param,

      unsigned int interval, unsigned short flags);
Changed lines 1227-1231 from:
Dedicated Timer Process

Since, by default, all the registered timer functions are called from within the same process context, in case you are writing a timer process that is doing I/O, it is better to register an entirely new process where to run your code, since your function might slow down all the other timer functions running in OpenSIPS.
Registering a new timer process can be done by calling :

to:


Important to note here that all the above timer related functions MUST be called in the context of the "attendant" process, before forking any other workers (so either from your module's mod_init() callback or directly from the core, before forking).

Below is a code snippet exemplifying how the dialog module registers two timer functions:

  • one with second precision, responsible for cleaning up empty profile structures
  • one with microsecond precision, responsible for replicating profile counters to other cluster nodes
Changed lines 1239-1300 from:

/* Parameters :

      label - opaque string containing a short timer function description ( to be used for logging )
      f - the actual function to be called
      param - parameter to be provided to the timer function
      interval - the interval, in seconds, that the function needs to be called at
      flags - flags controlling process behavior. Currently only option is TIMER_PROC_INIT_FLAG , which leads to child_init to be called in the new timer process context. To be used when inside the timer you need to operate various I/O options which generally require a per process connection.

Returns :

      struct sr_timer_process pointer in case of success, or NULL in case of error.
  • /

void* register_timer_process(char *label, timer_function f, void* param,

      unsigned int interval, unsigned int flags);

(:sourceend:)
The output struct sr_timer_process pointer can be further use to group together multiple functions inside the same process, by calling : (:source lang=C -link -getcode :) /* Parameters:

      label – opaque string containing a short timer function description ( to be used for logging )
      f – the actual function to be called
      param – parameter to be provided to the timer function
      interval – the interval, in seconds, that the function needs to be called at
      timer – the  struct sr_timer_process pointer obtained by previously calling  register_timer_process

Returns:

      0 in case of success, negative code in case of internal error.
  • /

int append_timer_to_process( char *label, timer_function f, void* param,

      unsigned int interval, void *timer);

(:sourceend:)

Important to note here that all the above timer related functions MUST be called in the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).

Below is a code snippet exemplifying how the dialog module's code used for registering two timers, with an option to either use the global timer process or to have it's own separate timer : (:source lang=C -link -getcode :) if (dlg_have_own_timer_proc) {

      LM_INFO("Running with dedicated dialog timer process\n"); 
      dlg_own_timer_proc = register_timer_process( "dlg-timer", 
            dlg_timer_routine, NULL,1,TIMER_PROC_INIT_FLAG ); 
      if (dlg_own_timer_proc == NULL) { 
            LM_ERR("Failed to init dialog own timer proc\n"); 
            return -1; 
      } 
      if (append_timer_to_process("dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval,dlg_own_timer_proc) < 0) { 
            LM_ERR("Failed to append ping timer \n"); 
            return -1; 
      }

} else {

      if ( register_timer( "dlg-timer", dlg_timer_routine, NULL, 1)<0 ) { 
            LM_ERR("failed to register timer \n"); 
            return -1; 
      } 

      if ( register_timer( "dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval)<0) { 
            LM_ERR("failed to register timer 2 \n"); 
            return -1; 
      } 

}

to:
    if (register_timer("dialog-repl-profiles-timer", clean_profiles, NULL,
        repl_prof_timer_check, TIMER_FLAG_DELAY_ON_DELAY) < 0) {
        LM_ERR("failed to register profiles utimer\n");
        return -1;
    }

    ...

    if (register_utimer("dialog-repl-profiles-utimer", broadcast_profiles, NULL,
        repl_prof_utimer * 1000, TIMER_FLAG_DELAY_ON_DELAY) < 0) {
        LM_ERR("failed to register profiles utimer\n");
        return -1;
    }
November 22, 2019, at 01:37 PM by liviu -
Changed line 1171 from:
  • from a very limited number of processes, you must do a lock_start_read() which sometimes transitions into a lock_stop_read() + lock_start_write()
to:
  • from a very limited number of processes, you must do a lock_start_read() which cannot transition into a writer role using lock_stop_read() + lock_start_write(), because temporarily exiting the critical region is unacceptable. At the same time, always doing a lock_start_write() from these processes would cause unnecessary overhead, since the writer transition is very unlikely to happen.
November 22, 2019, at 01:33 PM by liviu -
Changed line 1 from:
Documentation -> Manuals -> OpenSIPS Development Manual
to:
Documentation -> Manuals -> OpenSIPS 3.1 Development Manual
Changed lines 7-8 from:
OpenSIPS Development Manual
to:
OpenSIPS 3.1 Development Manual
Changed lines 25-26 from:

The tutorial is accurate as of OpenSIPS 1.11

to:

The tutorial is accurate as of OpenSIPS 3.1 LTS

Changed lines 825-835 from:

OpenSIPS has it's own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the usage case and the menuconfig provided compilation flags, the OpenSIPS generic locks can be converted either to busy locks, futexes, SysV locks, etc.

to:

OpenSIPS has its own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the use case and the menuconfig-provided compilation flags, the OpenSIPS generic locks can be switched to:

  • architecture specific locks
    • with futexes
    • with adaptive waiting (yield execution instead of blocking)
    • with busy-waiting
  • SysV locks
  • pthread mutexes
  • POSIX semaphores
  • umutexes (FreeBSD)
Changed lines 1067-1070 from:

A readers-writer lock is like a mutex, in that it controls access to a shared resource, allowing concurrent access to multiple threads for reading but restricting access to a single thread for writes (or other changes) to the resource.
This can prove very useful when having an use case where all the OpenSIPS processes need read-only access to a resource, but you need to have an MI command to reload that resource ( eg. from a database ). In such scenarios, using a readers-writers lock can improve performance by a considerable margin.
The API can be used by including “rw_locking.h” . The OpenSIPS generic lock is defined by the rw_lock_t structure.

to:

A readers-writer lock is like a mutex, in that it controls access to a shared resource, allowing concurrent access to multiple threads for reading but restricting access to a single thread for writing to the resource.
This can prove very useful when having an use case where all the OpenSIPS processes need read-only access to a resource, but you need to have an MI command to reload that resource (e.g. from a database). In such scenarios, using a readers-writers lock can improve performance by a considerable margin.
The API can be used by including rw_locking.h . The OpenSIPS generic lock is defined by the rw_lock_t structure.

Added lines 1128-1173:


Taking reader/writer locking to a new level, for scenarios with N x guaranteed-to-be readers and 1 x reader which may occasionally want to transition into a writer role after grabbing the read lock (depending on some condition), we can avoid forcing our uncertain "1 x reader" to be a simple writer (and always hogging down all other readers) into being a "switchable reader", thanks to the below API functions: (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to acquire
  • /

void lock_start_sw_read(rw_lock_t * lock); (:sourceend:)
Our single, undecided reader may sometimes switch into writing mode using: (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to acquire
      write_status - the previous "write" status of the lock
  • /

void lock_switch_write(rw_lock_t * lock, int write_status); # this is actually a macro, so "write_status" is an output variable :) (:sourceend:)
After switching to write, switching back to read is mandatory before being able to release the lock: (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to acquire
      write_status - the previous "write" status of the lock
  • /

void lock_switch_read(rw_lock_t * lock, int previous_write_status); (:sourceend:)
Finally, we release the "switchable reading" lock: (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to acquire
      write_status - the previous "write" status of the lock
  • /

void lock_stop_sw_read(rw_lock_t * lock); (:sourceend:)

NB: the "switchable reading" API is only useful when all below conditions are true:

  • you are doing a lot of lock_start_read(), from lots of processes
  • from a very limited number of processes, you must do a lock_start_read() which sometimes transitions into a lock_stop_read() + lock_start_write()

... in all other reader/writer cases, just use the basic R/W locking API.

November 22, 2019, at 11:09 AM by liviu -
Deleted lines 821-885:

(:sourceend:) Then, each class of transformations should have it

's own functions enumeration, which is our case is enum _tr_uri_subtype .
transformations.c will also hold the implementation of any new transformations. parse_transformation will get called when the script needs to evaluate a transformation. There, we should add the matching for our new class of transformations :

(:source lang=C -link -getcode :) else if(tclass.len==3 && strncasecmp(tclass.s, "uri", 3)==0) {

      t->type = TR_URI; 
      t->trf = tr_eval_uri; 
      s.s = p; s.len = in->s + in->len - p; 
      p0 = tr_parse_uri(&s, t); 
      if(p0==NULL) 
            goto error; 
      p = p0; 

} (:sourceend:)
Here, there are two main functions of interest. First, there is the tr_parse_uri function responsible for matching the actual function that needs to get executed inside our class of transformation, doing whatever parsing that is necessary for the transformation and advancing the position of the current pointer in the transformation string. This is a fixup type function and is only evaluated once :

(:source lang=C -link -getcode :) p = in->s; name.s = in->s;

/* find next token */ while(*p && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') {

      LM_ERR("invalid transformation: %.*s\n", in->len, in->s);
      goto error;

} name.len = p - name.s; trim(&name);

if(name.len==4 && strncasecmp(name.s, "user", 4)==0) {

      t->subtype = TR_URI_USER;
      return p;

} (:sourceend:)
After we've decided what actually needs to be done and did all the parsing,the tr_eval_uri will be the one gets called at runtime and that actually evaluates our transformation and returns the new transformation output.
In our case, the function parses the provided string into a SIP URI, and then populates the output pv_value_t accordingly to the subtype provided : (:source lang=C -link -getcode :) /* make a PKG copy of the input */ _tr_uri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); memcpy(_tr_uri.s, val->rs.s, val->rs.len); _tr_uri.s[_tr_uri.len] = '\0'; ... ... /* parse the input URI */ if(parse_uri(_tr_uri.s, _tr_uri.len, &_tr_parsed_uri)!=0) {

      LM_ERR("invalid uri [%.*s]\n", val->rs.len,val->rs.s);
      return -1;

} ... ... /* zero out the output val */ memset(val, 0, sizeof(pv_value_t)); /* the output pvar will be a string */ val->flags = PV_VAL_STR;

switch(subtype) {

      case TR_URI_USER:
            val->rs = (_tr_parsed_uri.user.s)?_tr_parsed_uri.user:_tr_empty;
            break;

}

November 22, 2019, at 11:07 AM by liviu -
Changed line 745 from:

The so called transformations are methods operating directly on the OpenSIPS various pseudo-variables. A transformation takes as input the value of the provided pseudo-variable and processes it, outputing a 'transformed' version.

to:

Transformations are functions which can operate directly on any OpenSIPS pseudo-variable. A transformation takes as input the value of the pseudo-variable and processes it, outputting a transformed version.

Changed lines 747-751 from:

Transformation examples are the URI transformations ( working on a SIP URI, and they allow extracting various useful information from the URI, like the user , domain, various parameters, etc ), the S class of transformations ( work on generic strings, and provide various useful methods as fetching the length of the string, searching the first occurrence of a character inside the string, etc ), and many others – see http://www.opensips.org/Documentation/Script-Tran-1-10

to:

Some transformation examples:

  • the "uri" transformations (which work on SIP URIs, and they allow extracting various useful information, such as the URI's username or domain parts, various parameters, etc.)
  • the "s" class of transformations (which work on generic strings, and provide various useful methods as fetching the length of the string, searching the first occurrence of a character inside the string, etc.)
  • ... many others! – see http://www.opensips.org/Documentation/Script-Tran-3-1
Changed lines 756-757 from:

xlog(“Our variable has $(var(tutorial){s.len}) characters \n”);

to:

xlog("Our variable has $(var(tutorial){s.len}) characters\n");

  1. ... which will print "Our variable has 13 characters\n"
Changed line 762 from:

Note that transformations can be chained together, which will have an impact on the transformations C development

to:

Note that transformations can be chained together, so make sure to take this into account when implementing new ones!

Changed line 768 from:

xlog(“The username of our URI has $(var(our_uri){uri.user}{s.len}) characters \n”);

to:

xlog("The username of our URI has $(var(our_uri){uri.user}{s.len}) characters\n");

Changed lines 773-776 from:

We will further follow the implementation of the uri class of transformation, and then focusing on the user function.
Adding new classes of transformations is done in transformations.h , where , for our example's case, TR_URI was added. Then, each class of transformations should have it's own functions enumeration, which is our case is enum _tr_uri_subtype .
transformations.c holds the actual implementation of transformations. parse_transformation will get called when the script needs to evaluate a transformation. There, we should add the matching for our new class of transformations :

to:

We will further follow the implementation of the uri class of transformation, and then focus on the user function.
Adding new classes of transformations is done in transformations.c, by extending the core_trans[] array: (:source lang=C -link -getcode :) static trans_export_t core_trans[] = { ...

    {str_init("uri"), tr_parse_uri, tr_eval_uri},

... (:sourceend:) Notice that we supply a parsing function and an evaluation function.
The parsing function will be invoked with the full name of the transformation, so you can parse it and establish the actual function (transformation) to be invoked: (:source lang=C -link -getcode :) int tr_parse_uri(str* in, trans_t *t) {

    char *p;
    str name;
    tr_param_t *tp = NULL;

    if(in==NULL || in->s==NULL || t==NULL)
        return -1;
    p = in->s;
    name.s = in->s;

... (:sourceend:)
After we've decided what actually needs to be done and did all the parsing, the evaluation function, tr_eval_uri() will do all the weightlifting at runtime, as it will evaluate our transformation and return the new transformation output.
In our case, the function parses the provided string into a SIP URI, and then populates the output pv_value_t accordingly to the subtype provided : (:source lang=C -link -getcode :) /* make a PKG copy of the input */ _tr_uri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); ... memcpy(_tr_uri.s, val->rs.s, val->rs.len); _tr_uri.s[_tr_uri.len] = '\0'; ... /* parse uri -- params only when requested */

        if(parse_uri(_tr_uri.s, _tr_uri.len, &_tr_parsed_uri)!=0)
        {
            LM_ERR("invalid uri [%.*s]\n", val->rs.len,
                    val->rs.s);

... /* zero out the output val */ memset(val, 0, sizeof(pv_value_t)); /* the output pvar will be a string */ val->flags = PV_VAL_STR;

switch(subtype) {

      case TR_URI_USER:
            val->rs = (_tr_parsed_uri.user.s)?_tr_parsed_uri.user:_tr_empty;
            break;

... (:sourceend:) Then, each class of transformations should have it

's own functions enumeration, which is our case is enum _tr_uri_subtype .
transformations.c will also hold the implementation of any new transformations. parse_transformation will get called when the script needs to evaluate a transformation. There, we should add the matching for our new class of transformations :

November 21, 2019, at 06:48 PM by liviu -
Changed lines 498-499 from:

Now, under cfg.lex, we need to instruct the lexer to recognize our new token:

to:

Now, under cfg.lex, we need to instruct the lexer to recognize our new token:

Changed line 503 from:
to:

\\

Changed lines 522-523 from:

In the following step by step tutorial, we will follow the implementation of the xlog core parameter, which is used to print debugging information to the logging facility.

to:

In the following step by step tutorial, we will follow the implementation of the xlog core function, which is used to print information to the logging facility.

Changed line 525 from:

Note that xlog can receive either a single parameter ( the string to be printed ), or two parameters ( the log level and then the string to be printed ).

to:

Note that xlog can receive either a single parameter (the string to be printed), or two parameters (the log level and then the string to be printed).

Changed line 528 from:

First, the grammar will have to be extended. In cfg,y we have :

to:

First, we extend the lexer file with the new word. Under cfg.lex, we have:

Added lines 530-536:

XLOG "xlog" (:sourceend:)


Next, the grammar must be extended. In cfg.y, we have : (:source lang=C -link -getcode :)

Changed lines 540-543 from:

| XLOG LPAREN STRING RPAREN {

      mk_action1($$, XLOG_T, STR_ST, $3); }                             
 XLOG LPAREN STRING COMMA STRING RPAREN {                                        
      mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); } 
to:
        | XLOG LPAREN STRING RPAREN {
                mk_action1($$, XLOG_T, STR_ST, $3); }
        | XLOG LPAREN folded_string RPAREN {
                mk_action1($$, XLOG_T, STR_ST, $3); }
        | XLOG LPAREN STRING COMMA STRING RPAREN {
                mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); }
        | XLOG LPAREN STRING COMMA folded_string RPAREN {
                mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); }
Changed line 550 from:

Note the two different forms of xlog displayed above.

to:

Note the different ways of invoking xlog() we defined: the basic version with 1 parameter, the 2-parameter version and alternatives which allow it to receive multi-line strings. Notice that XLOG_T is a new enum value which we will define in route_struct.h.

Changed line 552 from:

From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done. The fixup part will be invoked only once, at script parsing.

to:

From the grammar, we will start building the actions. In route.c, we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done. The fixup part will be invoked only once, at script parsing.

Changed lines 554-576 from:

case XLOG_T:

      s.s = (char*)t->elem[1].u.data; 
      if (s.s == NULL) { 
            /* commands have only one parameter */ 
            s.s = (char *)t->elem[0].u.data; 
            s.len = strlen(s.s); 
            if(s.len==0) { 
                  LM_ERR("param is empty string!\n"); 
                  return E_CFG; 
            } 

            /* parse the format provided to xlog - we can have variables inside */
            if(pv_parse_format(&s ,&model) || model==NULL) { 
                  LM_ERR("wrong format [%s] for value param!\n", s.s); 
                  ret=E_BUG; 
                  goto error; 
            } 

            /* overwrite the data that will be passed to contain our new parsed model */
            t->elem[0].u.data = (void*)model; 
            t->elem[0].type = SCRIPTVAR_ELEM_ST; 
      } else {
            /* two parameters */
to:
            case XLOG_T:
                s.s = (char*)t->elem[1].u.data;
                if (s.s == NULL) {
                    /* commands have only one parameter */
                    s.s = (char *)t->elem[0].u.data;
                    s.len = strlen(s.s);
                    if(s.len==0)
                    {
                        LM_ERR("param is empty string!\n");
                        return E_CFG;
                    }

                    if(pv_parse_format(&s ,&model) || model==NULL)
                    {
                        LM_ERR("wrong format [%s] for value param!\n", s.s);
                        ret=E_BUG;
                        goto error;
                    }

                    t->elem[0].u.data = (void*)model;
                    t->elem[0].type = SCRIPTVAR_ELEM_ST;
                } else {
                    /* there are two parameters */
Changed line 579 from:

In action.c we will have to add the actual code that needs to get executed when the function gets called from the OpenSIPS script at runtime :

to:

In action.c we will have to add the actual code that needs to get executed when the function gets called from the OpenSIPS script at runtime:

Changed lines 582-607 from:
      /* add helpers for tracing the script */
      script_trace("core", "xlog", msg, a->line) ;
      if (a->elem[1].u.data != NULL) {
            /* we have two parameters */ 

            /* do security checks for the types of the provided parameters, second param has to be a SCRIPTVAR model as we've coded in the fixup */
            if (a->elem[1].type != SCRIPTVAR_ELEM_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type); 
                  ret=E_BUG; 
                  break; 
            }
            /* log level should be a plaintext string */ 
            if (a->elem[0].type != STR_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); 
                  ret=E_BUG; 
                  break; 
            }

            /* call our C code function implementing the desired actions */
            if (xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data) < 0) { 
                  LM_ALERT("Cannot print xlog debug message"); 
                  break;
            }
      } else {
            /* one parameter case */
      }
to:
            script_trace("core", "xlog", msg, a->file, a->line) ;
            if (a->elem[1].u.data != NULL) {
                if (a->elem[1].type != SCRIPTVAR_ELEM_ST)
                {
                    LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type);
                    ret=E_BUG;
                    break;
                }
                if (a->elem[0].type != STR_ST)
                {
                    LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type);
                    ret=E_BUG;
                    break;
                }
                ret = xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data);
                if (ret < 0)
                {
                    LM_ERR("error while printing xlog message\n");
                    break;
                }
            } else {
November 21, 2019, at 06:11 PM by liviu -
Changed line 485 from:

In the following step by step tutorial, we will follow the implementation of the children core parameter, which is an integer controlling the number of OpenSIPS processes per UDP interface. \\

to:

In the following step by step tutorial, we will follow the implementation of the udp_workers core parameter, which is an integer controlling the number of OpenSIPS processes per UDP interface. \\

Changed line 489 from:

extern int children_no;

to:

extern int udp_workers_no;

Changed line 495 from:

int children_no = 8;

to:

int udp_workers_no = 8;

Changed lines 498-499 from:

Next, we will have to modify the grammar in order to accept our new parameter. In cfg.y , first we will define our new token :

to:

Now, under cfg.lex, we need to instruct the lexer to recognize our new token:

Changed lines 501-502 from:

%token CHILDREN

to:

/* Default value in case the parameter is not set from the script */ UDP_WORKERS udp_workers

Changed lines 504-505 from:


And then we'll set the parsing rules for the new token

to:

Next, we will have to modify the grammar in order to accept our new parameter. In cfg.y , first we re-specify the lexer token:

Changed lines 508-510 from:

| CHILDREN EQUAL NUMBER { children_no=$3; } | CHILDREN EQUAL error { yyerror("number expected"); } (:sourceend:)

to:

%token UDP_WORKERS (:sourceend:)


Finally, we set the parsing rules for the new token: (:source lang=C -link -getcode :) | UDP_WORKERS EQUAL NUMBER { udp_workers_no=$3; } | UDP_WORKERS EQUAL error { yyerror("number expected"); }

October 18, 2019, at 07:43 PM by liviu -
Changed lines 182-183 from:

For parsing a specific header type, and extracting the header type relevant information, the parser/ folder contains all implementations for known headers. The naming convention is that parser/parse_X.h will expose the parsing for the X header name.

to:

For parsing a specific header type and extracting the header type relevant information, the parser/ directory contains all implementations for known headers. The naming convention is that parser/parse_X.h will expose the parsing for the header named X.

Changed lines 185-186 from:

For example, here is the implementation of the parsing of the TO header, exposed by parser/parse_to.h :

to:

For example, here is the implementation of the parsing of the To header, exposed by parser/parse_to.h :

Changed line 235 from:

LM_INFO(“The TO header tag value is %.*s \n”,get_to(msg)->tag_value.len, get_to(msg)->tag_value.s);

to:

LM_INFO("The To header tag value is %.*s\n", get_to(msg)->tag_value.len, get_to(msg)->tag_value.s);

Changed line 258 from:

Here is an example of parsing the TO URI :

to:

Here is an example of parsing the To header URI:

October 18, 2019, at 07:37 PM by liviu -
Changed line 131 from:

Also, important to note that there are two types of parsing:

to:

Also, it is important to note that there are two types of parsing:

Changed line 135 from:

Also, all parsing is done in a stateful manner, in the sense that the OpenSIPS parser knows which header was parsed and internally keeps a parsing bitmask. Thus, once the To header was first parsed, any subsequent attempts of parsing the To header will return immediately.

to:

All parsing is done in a stateful manner, in the sense that the OpenSIPS parser knows which header was parsed and internally keeps a parsing bitmask. Thus, once the To header was first parsed, any subsequent attempts of parsing the To header will return immediately.

Changed lines 140-141 from:

The generic parser of SIP headers is exposed by parser/msg_parser.h . The function to be used is :

to:

The generic SIP header parser is exposed by parser/msg_parser.h. The function to be used is:

Changed lines 158-159 from:

if(parse_headers(req,HDR_CALLID_F|HDR_TO_F|HDR_FROM_F,0)<0 || !req->callid || !req->to || !req->from) {

      LM_ERR("bad request or missing CALLID/TO/FROM hdr \n"); 
to:

if (parse_headers(req, HDR_CALLID_F|HDR_TO_F|HDR_FROM_F, 0) < 0 || !req->callid || !req->to || !req->from) {

      LM_ERR("bad request or missing CALLID/TO/FROM hdr\n"); 
Changed line 164 from:

HDR_EOH_F is exposed in case the developer wants to parse all the headers in the current SIP message.

to:

HDR_EOH_F can be used in order to parse all the headers in the current SIP message.

Changed line 172 from:

Upon return, if successful, the function will populate the respective hooks in the sip_msg structure for known headers.The hdr_field* structures are allocated in pkg memory, and will automatically be freed when the SIP message processing has finished.

to:

Upon return, if successful, the function will populate the respective hooks in the sip_msg structure for the known headers. The hdr_field* structures are allocated in pkg memory, and will automatically be freed when the SIP message processing has finished.

Changed lines 174-175 from:

After succesful parsing, the developer can access the header name and body like this :

to:

After successful parsing, the developer can access the header name and body as such:

Changed line 177 from:

LM_INFO(“The callid header name is %.*s and the callid header body is %.*s \n”, req->callid->name.len, req->callid->name.s, req->callid->bodylen. req->callid->body.s);

to:

LM_INFO("The callid header name is %.*s and the callid header body is %.*s\n", req->callid->name.len, req->callid->name.s, req->callid->bodylen. req->callid->body.s);

October 18, 2019, at 07:33 PM by liviu -
Changed lines 121-122 from:

Memory Allocators

to:
Memory Allocators
Changed lines 127-129 from:

The OpenSIPS SIP message parser is a lazy parser, which performs very well in terms of performance. The behavior is the following :

  • when the message is received, only the 'critical' headers are parsed ( eg : topmost VIA )
  • for known header types, when the developer wants to extract the first occurrence of a header, it does not have to parse the entire message, but rather the parser will stop parsing when the first needed header type was identified
to:

The OpenSIPS SIP message parser is a lazy parser, which performs very well in terms of performance. The behavior is the following :

  • when the message is received, only the critical headers are parsed (e.g. topmost Via header)
  • for known header types, when the developer wants to extract the first occurrence of a header, they do not have to parse the entire message, but rather the parser will stop parsing when the first needed header type was identified.
Changed lines 131-133 from:

Also, important to note that there are two types of parsing :

  • parsing in the sense of identifying header boundaries and separating between the header name and header body ( eg : parsing the 'To : vlad@opensips.org;tag=123\r\n' header, and identifying that first, the header is present, and secondly that the body is 'vlad@opensips.org;tag=123\r\n' )
  • in-depth parsing of a specific header type, for identifying header specific information ( in our example, extracting the to-tag in the TO header, etc ).
to:

Also, important to note that there are two types of parsing:

  • parsing in the sense of identifying header boundaries and separating between the header name and header body (e.g. parsing the 'To : vlad@opensips.org;tag=123\r\n' header, and identifying that first, the header is present, and secondly that the body is 'vlad@opensips.org;tag=123\r\n')
  • in-depth parsing of a specific header type, for identifying header specific information (in our example, extracting the to-tag in the To header, etc.).
Changed line 135 from:

Also, all parsing is done in a stateful manner, in the sense that the OpenSIPS parser know which header was parsed and by internally storing a bitmask. Thus, once the TO header was first parsed, any subsequent attempts of parsing the TO header will return immediately.

to:

Also, all parsing is done in a stateful manner, in the sense that the OpenSIPS parser knows which header was parsed and internally keeps a parsing bitmask. Thus, once the To header was first parsed, any subsequent attempts of parsing the To header will return immediately.

October 18, 2019, at 07:29 PM by liviu -
Added lines 120-123:

Memory Allocators

To find out more about the strengths and weaknesses of each memory allocator, be sure to read this comprehensive blog post

October 18, 2019, at 07:19 PM by liviu -
Changed line 55 from:

Note the common use case where, before forking OpenSIPS processes, the developer stores some static variables in the private memory of the main process. After forking, each the child process will inherit that private memory chunk and will have its individual copy of it.

to:

Common use case: before forking OpenSIPS processes, the developer stores some static variables in the private memory of the main process. After forking, each child process will have its own clone of the private memory chunk (same memory address pointer!).

October 18, 2019, at 07:17 PM by liviu -
Changed line 37 from:

OpenSIPS has it's own memory allocator, and this provides some important advantages over the system memory allocator :

to:

OpenSIPS has its own memory allocator. This provides some important advantages over the system memory allocator:

Changed line 40 from:
  • multiple allocators are available, each with different use cases ( options are the standard allocator, the memory debugging allocator – useful for debugging memory leaks and memory corruptions – and some others , check the menuconfig configurations options ).
to:
  • multiple allocators are available, each with different use cases (a standard allocator, a memory debugging allocator – useful for debugging memory leaks and memory corruptions, and a high-performance allocator – built for highly parallel workloads on multi-core systems).
Changed line 42 from:

Furthermore, since OpenSIPS is a multi-process application, the use of shared memory is required in many very common scenarios. The OpenSIPS allocators also hide the shared memory implementations details, and expose a very simple API, very similar to the system allocator.

to:

Furthermore, since OpenSIPS is a multi-process application, the use of shared memory is required in many very common scenarios. The OpenSIPS allocators also hide the shared memory implementations details and expose a very simple API, very similar to the system allocator.

Changed line 44 from:

All OpenSIPS allocators request from the system memory, at startup, the maximum configured memory for OpenSIPS, and then it manages it internally as OpenSIPS starts to use memory while processing traffic.

to:

On OpenSIPS startup, all custom allocators will request the maximum configured OpenSIPS memory from the operating system, and then proceed to manage it internally as OpenSIPS starts to use memory while processing traffic.

Changed line 46 from:

From OpenSIPS perspective, there are two types of memory, one that is related to the context of a single Process ( Private Memory ) , and another that is shared between all OpenSIPS processes ( Shared Memory ).

to:

From a development perspective, there are two types of memory: one that is tied to the context of a single Process (Private Memory), and another that is shared between all OpenSIPS processes (Shared Memory).

Changed line 51 from:

Private memory is only specific to a single OpenSIPS process. Since it has no visibility outside the current process, no locking mechanisms are required while using such memory.

to:

Private memory is only specific to a single OpenSIPS process. Since it has no visibility outside the current process, no locking mechanisms are required while managing such memory, hence allocating private memory will be faster than allocating shared memory.

Changed line 55 from:

Note the common use case where , before forking OpenSIPS processes, the developer stores some static variables in private memory of the main process. After forking, all the child processes will inherit that private memory chunk and each will have it's individual copy of it.

to:

Note the common use case where, before forking OpenSIPS processes, the developer stores some static variables in the private memory of the main process. After forking, each the child process will inherit that private memory chunk and will have its individual copy of it.

July 06, 2014, at 09:09 PM by 213.233.84.73 -
Added lines 2980-3251:

The Dialog module API is exported by the modules/dialog/dlg_load.h file.
The dialog module provides dialog awareness to the OpenSIPS proxy. Its functionality is to keep track of the current dialogs and to offer information about them (like how many dialogs are active).
Aside from tracking, the dialog module offers functionalities like flags and attributes per dialog (persistent data across dialog), dialog profiling and dialog termination (on timeout base or external triggered).
First, you will have to bind to the RR module's API and get the structure which you will further use. The binding function is :

(:source lang=C -link -getcode :) /* Parameters : dlgb is the API output to be further used Returns the 0 in case of success and -1 in case of failure

  • /

static inline int load_dlg_api( struct dlg_binds *dlgb );

(:sourceend:)
The dlg_binds structure is exemplified below :

(:source lang=C -link -getcode :) struct dlg_binds {

        register_dlgcb_f     register_dlgcb;
        create_dlg_f         create_dlg;
        get_dlg_f            get_dlg;
        add_profiles_f       add_profiles;
        search_dlg_profile_f search_profile;
        set_dlg_profile_f    set_profile;
        unset_dlg_profile_f  unset_profile;
        get_profile_size_f   get_profile_size;
        store_dlg_value_f    store_dlg_value;
        fetch_dlg_value_f    fetch_dlg_value;
        terminate_dlg_f      terminate_dlg;

        match_dialog_f       match_dialog;
        validate_dialog_f    validate_dialog;
        fix_route_dialog_f   fix_route_dialog;

}; (:sourceend:)
Find below the API function signatures along with their usage :

(:source lang=C -link -getcode :) /* To be used to register a new dialog based callback. The function returns 0 on success. Otherwise, -1 is returned.

Parameters :

        * struct dlg_cell *dlg : the dialog that the registered callback belongs to
        * int cb_types : The type of registered callback ( can be a bitmask of multiple callback types). The options are as follows :
                        * DLGCB_LOADED - callback will get called when a new dialog is loaded into memory at startup ( from a database ) or at runtime ( from a database via external MI call or via the binary replication interface ). This callback must be registered alone, and the dlg_cell* provided at registration time must be NULL ( since it's a global cb type, not associated to any particular dialog )
                        * DLGCB_CREATED - callback will get called when a new dialog is created. The callback will be called when the dialog is fully initialized ( create_dialog() was called either from script or from the API and the Transaction associated to the initial invite is fully initialised as well ). This callback must be registered alone, and the dlg_cell* provided at registration time must be NULL ( since it's a global cb type, not associated to any particular dialog )
                        * DLGCB_FAILED - callback will get called when a particular dialog fails to establish ( 3xx, 4xx, 5xx or 6xx reply received and relayed ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_CONFIRMED - callback will get called when a particular dialog gets established ( 2xx reply received ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_REQ_WITHIN - callback will get called when a sequential request is matched as belonging to a particular dialog ( either via loose_route() or match_dialog() calling from the script). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_TERMINATED - callback will get called when a dialog gets terminated. Reasons here are BYE routing or external Termination ( MI, API, etc ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_EXPIRED - callback will get called when a dialog lives past it's assigned timeout ( see the $DLG_timeout script pvar ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_EARLY - callback will get called when the first provisional reply ( 1xx ) is received for the registered dialog. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_RESPONSE_FWDED - callback will get called when a reply is forwarded during the initial state of the dialog setup ( usually provisional 180, 183, etc ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_RESPONSE_WITHIN - callback will get called for all replies forwarded by OpenSIPS for sequential requests belonging to the current dialog. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_MI_CONTEXT - callback will get called when the 'dlg_list_ctx' MI function is called. Useful when modules binded to the dialog module API want to append nodes to the dlg_list_ctx MI response tree. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_DESTROY - callback will get called when the dialog is getting ready to be destroyed. At the time of the callback calling, the dialog is unlinked from the main hash, but it is not freed yet. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_SAVED - callback will get called when the dialog information is synchronized to the database ( either initial insertion, updating various fields in the DB or removing the dialog from the DB ). This is a per dialog callback, so the first dlg parameter MUST be provided.
        func - the actual callback function that will be executed.
        param - parameter that will be sent to the callback when called
        free_func - function to free the callback parameter at destroy time.
  • /

typedef int (*register_dlgcb_f)(struct dlg_cell* dlg, int cb_types,

                dialog_cb func, void *param, param_free_cb free_func);

/* callback function prototype */ typedef void (dialog_cb) (struct dlg_cell* dlg, int type,

                struct dlg_cb_params * params);

/* function to free the callback param */ typedef void (param_free_cb) (void *param);

/*

        Creates the dialog structure for the current initial INVITE message and will keep track of the call for the rest of it's lifetime.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                msg : the initial INVITE sip message
                flags : flags altering the behavior of the create dialog. Options here are :
                        * DLG_FLAG_BYEONTIMEOUT - dialog will be terminated from the middle, by OpenSIPS, when the dialog lifetime is exceeded.
                        * DLG_FLAG_PING_CALLER - ping the caller with OPTIONS messages to detect if the call is still up
                        * DLG_FLAG_PING_CALLEE - ping the callee with OPTIONS messages to detect if the call is still up
  • /

typedef int (*create_dlg_f)(struct sip_msg *req,int flags);

/*

        Returns the current dialog pointer. In case of no created dialog or other internall errors, NULL is returned.
  • /

typedef struct dlg_cell *(*get_dlg_f) (void);

/*

        Parses and creates the provided profile definitions.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                profiles - the NULL terminated string containing one or multiple profile definitions ( sepparated by ';' )
                has_value - whether our profies will contain values with counters, or they will be just stand-alone counters
  • /

typedef int (*add_profiles_f)(char* profiles, unsigned int has_value);

/*

        Looks up and returns the profile definition associated to the provided name. In case the profile is not found, or of internal error, NULL is returned.
        Parameters :
                name : str containing the name of a single profile definition
  • /

typedef struct dlg_profile_table* (*search_dlg_profile_f)(str *name);

/*

        Sets the current dialog to belong in the provided profile definition.
        Returns 0 in case of success, and -1 otherwise.
        Parameters :
                msg : the SIP message currently being processed
                value : the value the dialog will be associated with within the provided profile
                profile : the main profile the dialog will be linked to
                is_replicated : whether or not this dialog was originated on the current machine, or we received it via replication mechanisms. Controls whether the cachedb counters should be increased for the current dialog profile or not.
  • /

typedef int (*set_dlg_profile_f)(struct sip_msg *msg, str *value,

                        struct dlg_profile_table *profile, char is_replicated);

/*

        The opposite of the set_dlg_profile_f API function.
        Returns 0 in case of success, and -1 otherwise.
        Parameters :
                msg : the SIP message currently being processed
                value : the value the dialog will be de-associated with within the provided profile
                profile : the main profile the dialog will be un-linked from
  • /

typedef int (*unset_dlg_profile_f)(struct sip_msg *msg, str *value,

                         struct dlg_profile_table *profile);

/*

        Returns the number of dialogs belonging to the current profile.
        Parameters :
                profile : the profile definition
                value : the value to filter the profile. Can be missing, the size of all individual values within a profile will be returned.
  • /

typedef unsigned int (*get_profile_size_f)(struct dlg_profile_table *profile,

                                                                                str *value);

/*

        Stores an opaque key-value mapping inside the dialog structure, which can be fetched at a later time based on the current dialog. If the key already exists, it will be overwritten.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                dlg : the dialog pointer to link the key to
                name : name of the key to store within the provided dialog
                val : the value to be mapped to the provided key
  • /

typedef int (*store_dlg_value_f)(struct dlg_cell *dlg,

                str *name, str *val);

/*

        Fetch a previously stored value within the provided dialog.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                dlg : the dialog pointer to fetch the key from
                name : the name of the key to fetch
                val : output parameter, the value of the key will be stored here
                val_has_buf - whether we have a buffer allocated for fetching the key's value or not. If 0, the dialog module will return a static buffer that it reuses for all key values.
  • /

typedef int (*fetch_dlg_value_f)(struct dlg_cell *dlg,

                str *name, str *val, int val_has_buf);

/*

        Terminates an ongoing dialog ( by sending BYE messages both ways )
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                h_entry : The hash bucket ID for the dialog that we want to terminate
                h_id : The ID of the dialog element within our hash bucket
                reason : An opaque string describing the reason for terminating the dialog. Can be later fetched from the script by the user
  • /

typedef int (*terminate_dlg_f)(unsigned int h_entry, unsigned int h_id,str *reason); (:sourceend:)
See below a simple example of binding to the Dialog API from another module and running a couple of dialog related operations. (:source lang=C -link -getcode :) ...

  1. include "../dialog/dlg_load.h"

... ... struct dlg_binds my_dlgb; ... ... int mod_init(void) {

        ...
        ...
        /* load the dialog API */
        if (load_dlg_api(&my_dlgb)!=0) {
                LM_ERR("failed to find dialog API - is dialog module loaded?\n");
                goto error;
        }

        /* make sure we get notified of all upcoming created dialogs */
        if (my_dlgb.register_dlgcb(NULL,DLGCB_CREATED,new_created_dialog_cb,NULL,NULL) != 0 ) {
                LM_ERR("Failed to register initial dlg callback \n");
                goto error;
        }

        ...
        ...

}

void new_created_dialog_cb(struct dlg_cell *did, int type,

                struct dlg_cb_params * params)

{

        time_t curtime;
        str create_time_key = str_init("created_at");
        str create_time;

        time(&curtime);
        LM_INFO("Dialog was created ! \n");

        if ( my_dlgb->register_dlgcb(did,
        DLGCB_CONFIRMED, dialog_confirmed_cb, NULL, NULL) != 0) {
                LM_ERR("Failed to register CB for dialog establishment \n");
                return;
        }

        if ( my_dlgb->register_dlgcb(did,
        DLGCB_REQ_WITHIN, dialog_sequential_cb, NULL, NULL) != 0) {
                LM_ERR("Failed to register CB for dialog sequential requests \n");
                return;
        }

        create_time.s = ctime(&curtime);
        create_time.len = strlen(create_time.s);

        if ( my_dlgb.store_dlg_value(did,&create_time_key,&create_time) != 0) {
                LM_ERR("Failed to store our create string key \n");
                return;
        }

}

void dialog_confirmed_cb(struct dlg_cell *did, int type,

                struct dlg_cb_params * params)

{

        str create_time_key = str_init("created_at");
        str create_time;

        if ( my_dlgb.fetch_dlg_value(did,&create_time_key,&create_time,0) != 0 ) {
                LM_ERR("Failed to fetch our create string key \n");
                return;
        }

        LM_INFO("The dialog was created at %.*s and is now established \n",create_time.len,create_time.s);

}

void dialog_sequential_cb(struct dlg_cell *did, int type,

                struct dlg_cb_params * params)

{

        struct sip_msg *msg = msg;
        str term_reason = "we HATE options from callee :D";

        if (msg->first_line.u.request.method_value == METHOD_OPTIONS && params->dir == DLG_DIR_UPSTREAM) {
                LM_INFO("Received OPTIONS sequential from callee. Terminating DLG because %.*s\n",
                term_reason.len,term_reason.s);

                if (my_dlgb.terminate_dlg(did->h_entry,did->h_id,&term_reason) != 0) {
                        LM_ERR("Failed to terminate the dialog \n");
                }
        }

} (:sourceend:)

July 06, 2014, at 08:54 PM by 213.233.84.73 -
Added lines 2932-2977:

(:source lang=C -link -getcode :) ...

  1. include "../rr/api.h"

... struct rr_binds my_rrb; ... ... int mod_init(void) {

        ...
        ...
        /* load the RR API */
        if (load_rr_api( &my_rrb )!=0) {
            LM_ERR("can't load RR API\n");
            goto error;
        }

        if (!my_rrb.append_fromtag) {
            LM_ERR("The append_fromtag parameter is not set, but we need it for detecting the direction of requests \n");
            goto error;
        }
        ...
        ...
        /* register a RR callback */
        if (my_rrb.register_rrcb(my_callback,0,0))!=0) {
            LM_ERR("can't register RR callback\n");
            goto error;
        }
        ...
        ...

}

void my_callback(struct sip_msg* msg,str* rr_param,void *param) {

        str name = str_init("ftag");
        str val;

        LM_INFO("Received a new sequential request from %s\n",
        my_rrb.is_direction( msg, RR_FLOW_UPSTREAM)?"callee":"caller");

        if (my_rrb.get_route_param(msg,&name,&val) == 0) {
                LM_INFO("We have the ftag parameter with value [%.*s]\n",val.len,val.s);
        }

} (:sourceend:) \\

July 06, 2014, at 08:52 PM by 213.233.84.73 -
Changed lines 2770-2771 from:

(:sourceend:

to:

/* Adds a parameter to the requests's Record-Route URI. The API supports the use case

   where the Record-Routed header will be further added.
   The function is to be used for marking certain dialogs that can
   be identified from the sequential requests - since the Route
   headers in the sequential requests will also contain our added
   params, which we'll be able to fetch with get_route_param ( see below )

   The function returns 0 on success. Otherwise, -1 is returned.

   Parameters :
     * struct sip_msg* msg - request that will has the parameter
       “param” added to its Record-Route header.
     * str* param - parameter to be added to the Record-Route
       header - it must be in “;name=value” format.
  • /

typedef int (*add_rr_param_t)(struct sip_msg* msg, str* param);

/* The function checks for the request “msg” if the URI parameters

   of the local Route header (corresponding to the local server)
   matches the given regular expression “re”. It MUST be call
   after the loose_route was done.

   The function returns 0 on success. Otherwise, -1 is returned.

   * struct sip_msg* msg - request that will has the Route
       header parameters checked.
   * regex_t* re - compiled regular expression to be checked
       against the Route header parameters.
  • /

int (*check_route_param_t)(struct sip_msg* msg, regex_t* rem);

/* The function checks the flow direction of the request “msg”. As

   for checking it's used the “ftag” Route header parameter, the
   append_fromtag (see Section 1.4.1, “append_fromtag (integer)”
   module parameter must be enables. Also this must be call only
   after the loose_route is done.

   The function returns 0 if the “dir” is the same with the
   request's flow direction. Otherwise, -1 is returned.

   Meaning of the parameters is as follows:
     * struct sip_msg* msg - request that will have the direction
       checked.
     * int direction - direction to be checked against. It may be
       RR_FLOW_UPSTREAM ( from callee to caller ) or
       RR_FLOW_DOWNSTREAM ( from caller to callee ).
  • /

typedef int (*is_direction_t)(struct sip_msg* msg, int direction);

/*

   The function search in to the “msg”'s Route header parameters
   the parameter called “name” and returns its value into “val”.
   It must be call only after the loose_route is done.

   The function returns 0 if parameter was found (even if it has
   no value). Otherwise, -1 is returned.

   Meaning of the parameters is as follows:
     * struct sip_msg* msg - request that will have the Route
       header parameter searched.
     * str *name - contains the Route header parameter to be
       serached.
     * str *val - returns the value of the searched Route header
       parameter if found. It might be empty string if the
       parameter had no value.
  • /

typedef int (*get_route_param_t)(struct sip_msg*, str*, str*);

/*

   The function register a new callback (along with its
   parameter). The callback will be called when a loose route will
   succesfully be performed for the local address.

   The function returns 0 on success. Otherwise, -1 is returned.

    Meaning of the parameters is as follows:
     * rr_cb_t func - callback function to be registered.
     * void *param - parameter to be passed to the callback
       function.
     * short prior - parameter to set the priority. The callbacks
        will be executed in order from small to big priority - thus
        to be used for ordering callbacks that depend on each other.
  • /

typedef int (*register_rrcb_t)( rr_cb_t func, void *param, short prior);

/* Function to be registered as callback within the RR API :

     * struct sip_msg* req - request that is currently being processed
     * str *rr_param - the parameters in our server's Route header
     * str *param - the custom parameter provided at the callback registration
  • /

typedef void (rr_cb_t) (struct sip_msg* req, str *rr_param, void *param);

/*

   Function used to fetch the far-end remote target for the current message.
   Depending on the type routing done ( see the routing_type API member )
   the remote target can be either in the initial Request URI, in the current
   Request-URI or in the last route header. The API function take care to
   correctly identify which scenario is correct.
   The API function MUST be called after loose_route() was called.

   The function returns the str pointer with the remote target, or NULL in case of error.

   Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request that the remote target will be extracted from
  • /

typedef str* (*get_remote_target_t)(struct sip_msg* msg);

/*

    Function used to fetch the route set from the current SIP message.
    The function takes into account the actual loose_route() done, and properly discards
    the proxy's own Route headers from the SIP message. Thus, the function must be called
    after loose_route() was done.

    The function will return an array of str structures, or NULL in case of error. The
    nr_routes parameter will indicate the size of the returned array

    Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request that the remote target will be extracted from
        -* int* nr_routes - the size of the returned array
  • /

typedef str* (*get_route_set_t)(struct sip_msg*,int *nr_routes);

/*

    Function to be used when for routing a request according to the Route headers present
    in it and to the type of Routing ( loose vs strict ) that needs to be used.

    The function will return 0 in case of success ( request is succesfully routed ). Otherwise,
    -1 is returned.

   Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request to be routed
  • /

typedef int (*loose_route_t)(struct sip_msg* msg);

/*

    Function to be used when record-routing an initial request. The function will add
    one or two Record-Route headers , depending if there are any interface changes and
    if r2 is enabled. Also, if any parameters are provided, they will be added to all the
    Record-Route headers that the function internally adds.

    Returns 0 in case of success. Otherwise, -1 will be returned.

    Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request to be record routed
        -* str* params - parameters to be added to the Record-Route headers
  • /

typedef int (*record_route_t)(struct sip_msg* msg, str* params);

(:sourceend:)
See below an example of binding to the RR API from another module, registering a callback and then checking the direction of the sequential request and also checking for the existence of a certain parameter.

July 06, 2014, at 08:48 PM by 213.233.84.73 -
Changed lines 2730-2771 from:
to:


(:source lang=C -link -getcode :) /* Parameters : rrb is the API output to be further used

Returns : 0 in case of success and -1 in case of failure

  • /

inline static int load_rr_api( struct rr_binds *rrb ); (:sourceend:)
The rr_binds structure is exemplified below : (:source lang=C -link -getcode :) struct rr_binds {

        add_rr_param_t      add_rr_param;
        check_route_param_t check_route_param;
        is_direction_t      is_direction;
        get_route_param_t   get_route_param;
        register_rrcb_t     register_rrcb;
        get_remote_target_t get_remote_target;
        get_route_set_t     get_route_set;
        /* whether or not the append_fromtag parameter is enabled in the RR module */
        int                 append_fromtag;
        /* the number of routes removed within the loose routing process */
        int*                removed_routes;
        /* the type of routing done, when comparing the previous and the next hop
        Both can be either strict or loose routers, thus here we have 4 different options :
        ROUTING_LL - loose to loose routing
        ROUTING_SL - strict to loose routing
        ROUTING_SS - strict to strict routing
        ROUTING_LS - loose to strict routing
        */
        int*                routing_type;

        loose_route_t       loose_route;
        record_route_t      record_route;

}; (:sourceend:)
Find below the API function signatures along with their usage : (:source lang=C -link -getcode :)

(:sourceend:

July 06, 2014, at 08:43 PM by 213.233.84.73 -
Added lines 2716-2720:

Within OpenSIPS, one modules might need to access the functionality of another module ( one very common example are modules desiring to do operations on a per dialog basis, thus needing part of the dialog module functionality ). Instead of directly accessing the functionality from within the target module, OpenSIPS heavily uses the concept of a 'module exported API'.
The common approach used throughout OpenSIPS is that the target module should implement a form of loading it's API - which in fact translates into populating a structure with pointers of the functions that need to be exported, as well as various other structure members that indicate various behaviour.
The module that needs to operate with the above API should first call ( within it's mod_init ) the function to bind to the needed module's API, and then operate with the received structure.
Find below the most heavily used module APIs in OpenSIPS.

Added lines 2724-2730:

The RR ( Record Route ) module API is exported by the modules/rr/api.h file.
From a general functionality point of view, the RR module is the one responsible for controlling the Record-Route part for requests, and then routing the sequential requests based on the Route headers. The RR module is the simplest module that provides very simple dialog-aware functionality, by allowing the storage of information ( parameters ) in the Record-Route headers, which can be later retrieved from the Route headers.
First, you will have to bind to the RR module's API and get the structure which you will further use. The binding function is :

June 17, 2014, at 05:04 PM by 89.120.101.121 -
Changed line 1933 from:

rows = RES_ROWS(res);

to:

rows = RES_ROWS(db_res);

June 15, 2014, at 09:02 PM by 188.27.106.202 -
Changed lines 15-19 from:

TBD
TBD
TBD \\

to:

The focus of the following document will be on the general architecture of OpenSIPS, as well as presenting all the major components and APIs that OpenSIPS exposes for building new modules / features.
The tutorial does not aim to teach Linux / C programming. Below are a list of prerequisites :

  • C programming under Linux
  • Multi-Process programming under Linux
  • Basic Networking Concepts
  • Basic SIP knowledge


The tutorial is accurate as of OpenSIPS 1.11

June 15, 2014, at 08:18 PM by 188.27.106.202 -
Added lines 2185-2262:

For creating and sending a new event, the following methods are to be used : (:source lang=C -link -getcode :) /**

 * bin_init - begins the construction of a new binary packet (header part):
 *
 * +-------------------+------------------------------------------------------+
 * |  8-byte HEADER    |                 BODY                max 65535 bytes  |
 * +-------------------+------------------------------------------------------+
 * | PK_MARKER |  CRC  | LEN | MOD_NAME | CMD | LEN | FIELD | LEN | FIELD |...|
 * +-------------------+------------------------------------------------------+
 *
 * @param: { LEN, MOD_NAME } + CMD
 */

int bin_init(str *mod_name, int cmd_type)

/*

 * copies the given string at the 'cpos' position in the buffer
 * allows null strings (NULL content or NULL param)
 *
 * @return: 0 on success
 */

int bin_push_str(const str *info)

/*

 * adds a new integer value at the 'cpos' position in the buffer
 *
 * @return: 0 on success
 */

int bin_push_int(int info)

/**

 * bin_send - computes the checksum of the current packet and then
 * sends the packet over UDP to the @dest destination
 *
 * @return: number of bytes sent, or -1 on error
 */

int bin_send(union sockaddr_union *dest) (:sourceend:)
On the receiving end, the developer must first register a callback that will be trigger when receiving special types of BIN message, by using the following :

(:source lang=C -link -getcode :) /**

 * bin_register_cb - registers a module handler for specific packets
 * @mod_name: used to classify the incoming packets
 * @cb:       the handler function, called once for each matched packet
 *
 * @return:   0 on success
 */

int bin_register_cb(char *mod_name, void (*cb)(int)) (:sourceend:)
The callback will only be triggered for the mod_name class of BIN packages, and also the callback will receive the packet type as well, in order to be able to differentiate between multiple types of events ( eg. create, update, delete, etc ).
Afterwards, you should use the pop methods for extracting the contents of the package : (:source lang=C -link -getcode :) /*

 * pops an str from the current position in the buffer
 * @info:   pointer to store the result
 *
 * @return: 0 on success
 *
 * Note: The pointer returned in @info str is only valid for the duration of
 *       the callback. Don't forget to copy the info into a safe buffer!
 */

int bin_pop_str(str *info)

/*

 * pops an integer value from the current position in the buffer
 * @info:   pointer to store the result
 *
 * @return: 0 on success
 */

int bin_pop_int(void *info) (:sourceend:)
See the main page dedicated to the Binary Interface on how to then configure OpenSIPS listeners for the binary packages.

June 15, 2014, at 04:56 PM by 188.27.106.202 -
Added lines 2177-2184:

The Binary Internal Interface is an OpenSIPS core interface which offers an efficient way for communication between individual OpenSIPS instances.
This is especially useful in scenarios where realtime data (such as dialogs) cannot be simply stored in a database anymore, because failover would require entire minutes to complete. This issue can be solved with the new internal binary interface by replicating all the events related to the runtime data (creation / updating / deletion) to a backup OpenSIPS instance.
The BIN interface functionality is exported by the bin_interface.h file.
Using the interface has two steps :

  • creating and sending the new event from the Active OpenSIPS server
  • receiving and processing the event in the Backup OpenSIPS server
June 15, 2014, at 04:46 PM by 188.27.106.202 -
Changed lines 2628-2632 from:
Dialog Module
to:
Dialog Module

Video Tutorial

A full video tutorial ( 7 video sessions of 1-2 hours ) going through the OpenSIPS development process can be found here , along with some source code examples used in the video tutorial.

June 15, 2014, at 04:38 PM by 188.27.106.202 -
Changed line 2436 from:

As can be seen, all the OpenSIPS module functions receive string parameters ONLY, and a function can have a maximum of 6 parameters. The SIP message that is currently being processed is also transmitted as the first parameter, although this is transparent to the script writer ( he just provides the parameters idx 1 to 5 )

to:

typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, char*, char*);

Added lines 2438-2439:


As can be seen, all the OpenSIPS module functions receive string parameters ONLY, and a function can have a maximum of 6 parameters. The SIP message that is currently being processed is also transmitted as the first parameter, although this is transparent to the script writer ( he just provides the parameters idx 1 to 5 )

June 15, 2014, at 04:34 PM by 188.27.106.202 -
Changed lines 2538-2539 from:

For the full description of the used structures and functions, please see the generic Management Interface API section.

to:

For the full description of the used structures and functions, please see the generic Management Interface API section.

Changed line 2548 from:

For the full description of the used structures and functions for statistics, please see the generic Statistics API section. \\

to:

For the full description of the used structures and functions for statistics, please see the generic Statistics API section. \\

June 15, 2014, at 04:34 PM by 188.27.106.202 -
Added lines 1537-1545:


For fetching the mynewstat statistic exported by the mynewmod module, one can use the opensipsctl like this :
opensipsctl fifo get_statistics mynewmod mynewstat
For fetching all the statistics exported by the mynewmod module, you can use
opensipsctl fifo get_statistics mynewmod:

June 15, 2014, at 04:31 PM by 188.27.106.202 -
Changed lines 1233-1235 from:

Statistics API

OpenSIPS exposes a statistics API that can be used both from the core or the modules. The statistics are essentially counters that will be internally incremented/decremented by OpenSIPS and that can be fetched by the outside world ( via the MI interface ) for understanding the OpenSIPS load / health status / etc.

to:

Management Interface API

The Management Interface is the abstract layer that is commonly used to control and monitor OpenSIPS. The MI Interface supports multiple actual back-ends ( eg. FIFO, Datagram, XMLRPC, HTTP GET JSON, etc ) - due to the modularity of the interface and also due to the clear separation between the logic and the transport layer, the developer just defines the functions to be externally called, and then it is up to the OpenSIPS script writer to chose what transport he will actually use for controlling OpenSIPS.

Changed lines 1237-1239 from:

The advantages of using the OpenSIPS Statistics API instead of regular counters is :

  • easily fetched from the MI Interface
  • on supported architectures, the statistics do not use an explicit lock ( the consistency is ensured by employing assembly code ), thus you will get better performance
to:

The MI interface heavily uses trees :

  • the Interface will provide as input a tree with the parameters provided by the user
  • an MI function has to also return a tree, which will then be converted by the transport module to it's specific representation
Changed lines 1241-1242 from:

The most important structures used for extending statistics are exported by statistics.h :

to:

Further on we will focus on the core MI functions, with a specific focus on the debug function. Note that modules can ( an commonly ) also export MI functions - see the Modules Development MI functions topic for more information on that.
The structures commonly used for exporting MI functions are found in mi/mi.h :

Changed lines 1246-1251 from:

typedef struct stat_export_ {

        char* name;                /* null terminated statistic name */
        unsigned short flags;      /* flags */
        stat_var** stat_pointer;   /* pointer to the variable's mem location *
                                    * NOTE - it's in shm mem */

} stat_export_t;

to:

typedef struct mi_export_ {

        /* the name of the function ( users will call this from their transport of choice */
        char *name;
        /* short description of the usage of this function */
        char *help;
        /* actual function that will get called */
        mi_cmd_f *cmd;
        /* flags for this function. Currently options are :
             - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour ( eg: MI functions that send SIP messages and wait for their reply )
             - MI_NO_INPUT_FLAG - the function does not receive any parameters 
        */
        unsigned int flags;
        /* parameter that will be passed when the cmd function gets called */
        void *param;
        /* the initialization function to be called by OpenSIPS ( one time ) */
        mi_child_init_f *init_f;

}mi_export_t;

/* Example of core MI exported function */ static mi_export_t mi_core_cmds[] = {

        { "uptime", "prints various time information about OpenSIPS - "
                "when it started to run, for how long it runs",
                mi_uptime,     MI_NO_INPUT_FLAG,  0,  init_mi_uptime },
        { "version", "prints the version string of a runningOpenSIPS",
                mi_version,    MI_NO_INPUT_FLAG,  0,  0 },
        { "pwd", "prints the working directory of OpenSIPS",
                mi_pwd,        MI_NO_INPUT_FLAG,  0,  0 },

... ... ... /* For exporting the populated array of MI functions Parameters :

      mod_name : the name of the module exporting these functions
      mis : the array of exported MI functions

Returns :

      0 on success, negative in case of error
  • /

int register_mi_mod( char *mod_name, mi_export_t *mis);

/* Example of usage */ if (register_mi_mod( "core", mi_core_cmds)<0) {

      LM_ERR("unable to register core MI cmds\n");
      return -1;  

}

Changed lines 1292-1293 from:

For example, the core stats exported by OpenSIPS are defined in the following array :

to:

The structures commonly used for implementing MI functions are also found in mi/mi.h :

Changed lines 1294-1332 from:

stat_var* rcv_reqs; stat_var* rcv_rpls; stat_var* fwd_reqs; stat_var* fwd_rpls; stat_var* drp_reqs; stat_var* drp_rpls; stat_var* err_reqs; stat_var* err_rpls; stat_var* bad_URIs; stat_var* unsupported_methods; stat_var* bad_msg_hdr;

stat_export_t core_stats[] = {

        {"rcv_requests" ,         0,  &rcv_reqs              },
        {"rcv_replies" ,          0,  &rcv_rpls              },
        {"fwd_requests" ,         0,  &fwd_reqs              },
        {"fwd_replies" ,          0,  &fwd_rpls              },
        {"drop_requests" ,        0,  &drp_reqs              },
        {"drop_replies" ,         0,  &drp_rpls              },
        {"err_requests" ,         0,  &err_reqs              },
        {"err_replies" ,          0,  &err_rpls              },
        {"bad_URIs_rcvd",         0,  &bad_URIs              },
        {"unsupported_methods",   0,  &unsupported_methods   },
        {"bad_msg_hdr",           0,  &bad_msg_hdr           },
        {"timestamp",  STAT_IS_FUNC, (stat_var**)get_ticks   },
        {0,0,0}

}; (:sourceend:)

As note from the above structure, statistics can either be a simple counter ( eg. rcv_requests ), but it can also be a function. Statistics function might come in handy when the developer needs to do extra processing on the raw counters before providing the final output.


After defining your array of statistics that you want to export, one should use the following for exporting the stats to be accessible by all

(:source lang=C -link -getcode :) /*

to:

/*

Changed lines 1296-1297 from:
      module - a string describing the module the current statistics belong to. Will be used when fetching the statistics via MI
      stats - the statistics to be registered
to:
      input : the tree that contains the command paramenters
      param : the parameter provided at function registration
Changed line 1299 from:
      0 in case of success, negative in case of error
to:
      A mi_root tree containing the function reply
Changed lines 1301-1329 from:

int register_module_stats(char *module, stat_export_t *stats;

to:

typedef struct mi_root* (mi_cmd_f)(struct mi_root *input, void *param);

/* below are the used structures for representing the tree root and the tree nodes */ struct mi_root {

      /* int code - similar to SIP or HTTP code */
      unsigned int       code;
      /* string reason for code - similar to SIP or HTTP reason */
      str                reason;
      /* handler in case of asynchronous MI commands */ 
      struct mi_handler  *async_hdl;
      /* the actual root node in our tree */
      struct mi_node     node; 

};

struct mi_node {

      str value;
      str name;
      unsigned int flags;
      struct mi_node *kids;
      struct mi_node *next;
      struct mi_node *last; 
      struct mi_attr *attributes;

};

struct mi_attr{

        str name;
        str value;
        struct mi_attr *next;

};

Changed line 1332 from:

Note that register_module_stats will export the statistics, and also allocate them in SHM memory, for them to be accessible by all OpenSIPS processes.

to:

As can be noted from the above tree definition, a node has a name and a value associated to it,it can have one or multiple children ( stored in kids pointer ), and also it can have a list of key-value attributes associated to it.

Changed lines 1334-1341 from:

Important to note here that all the above statistics related functions MUST be called in the context of the attendant process before forking is done.


At runtime, the developer has access to the following functions for operating on statistics :

to:

For building the output MI tree, mi/tree.h and mi/attr.h expose the following functions :

Changed line 1336 from:

/*

to:

/* Use for creating a new output reply tree

Changed lines 1338-1343 from:
      var : the statistics to be updated
      n : the value ( if positive -> stat will be increment. negative -> stat will be decremented )
  • /

void update_stat(stat_var* var, int n);

/*

to:
      code : success code for this tree ( >=200<300 for success, anything else for errors )
      reason : string reasons representation for the code
      reason_len : length of the reason parameter

Returns :

      A new mi_root tree, or NULL in case of error. Note that this function will allocate the node in PKG and it typically has to be returned - the freeing will be done in the MI core, after the output tree is written by the transport module */

struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len);

/* Adding a new child node to our tree - typically first called to mi_root->node.kids

Changed lines 1347-1354 from:
      var : the statistics to be reseted
to:
      parent : the parent node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
Changed lines 1356-1358 from:

void reset_stat(stat_var* var);

/*

to:

struct mi_node *add_mi_node_child(struct mi_node *parent, int flags,

        char *name, int name_len, char *value, int value_len);

/* Adding a new sibling node to one of our nodes

Changed lines 1360-1362 from:
      var : the statistics to be fetched

Returns :

      statistic value
to:
      brother : the brother node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
Changed lines 1369-1382 from:

unsigned long get_stat_val(stat_var* var)

to:

struct mi_node *add_mi_node_sibling(struct mi_node *brother, int flags,

        char *name, int name_len, char *value, int value_len);

/* Adding a new attribute to one of our nodes

      node : the node we will be adding the key-value attribute to
      flags : Current options are :
                   MI_DUP_NAME : the name of this attribute needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current attribute needs to be duplicated in PKG
      name : the name of the current attribute
      name_len : length of the node's attribute name
      value : the value of the current value
      value_len : length of the node's attribute value
  • /

struct mi_attr *add_mi_attr(struct mi_node *node, int flags,

        char *name, int name_len, char *value, int value_len)
Deleted lines 1383-1390:

All statistics related code should be guarded by #ifdef STATISTICS , since the statistics are not a mandatory part of the OpenSIPS core ( they can be disabled from within menuconfig ).

Management Interface API

The Management Interface is the abstract layer that is commonly used to control and monitor OpenSIPS. The MI Interface supports multiple actual back-ends ( eg. FIFO, Datagram, XMLRPC, HTTP GET JSON, etc ) - due to the modularity of the interface and also due to the clear separation between the logic and the transport layer, the developer just defines the functions to be externally called, and then it is up to the OpenSIPS script writer to chose what transport he will actually use for controlling OpenSIPS.

Changed lines 1385-1392 from:

The MI interface heavily uses trees :

  • the Interface will provide as input a tree with the parameters provided by the user
  • an MI function has to also return a tree, which will then be converted by the transport module to it's specific representation


Further on we will focus on the core MI functions, with a specific focus on the debug function. Note that modules can ( an commonly ) also export MI functions - see the Modules Development MI functions topic for more information on that.
The structures commonly used for exporting MI functions are found in mi/mi.h :

to:

Further on, we will follow the implementation of the debug MI function. If called with no parameters, the function will return the current debug level in OpenSIPS. If called with one integer parameter, then the function will set the current debug level to the provided parameter.

Changed lines 1388-1430 from:

typedef struct mi_export_ {

        /* the name of the function ( users will call this from their transport of choice */
        char *name;
        /* short description of the usage of this function */
        char *help;
        /* actual function that will get called */
        mi_cmd_f *cmd;
        /* flags for this function. Currently options are :
             - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour ( eg: MI functions that send SIP messages and wait for their reply )
             - MI_NO_INPUT_FLAG - the function does not receive any parameters 
        */
        unsigned int flags;
        /* parameter that will be passed when the cmd function gets called */
        void *param;
        /* the initialization function to be called by OpenSIPS ( one time ) */
        mi_child_init_f *init_f;

}mi_export_t;

/* Example of core MI exported function */ static mi_export_t mi_core_cmds[] = {

        { "uptime", "prints various time information about OpenSIPS - "
                "when it started to run, for how long it runs",
                mi_uptime,     MI_NO_INPUT_FLAG,  0,  init_mi_uptime },
        { "version", "prints the version string of a runningOpenSIPS",
                mi_version,    MI_NO_INPUT_FLAG,  0,  0 },
        { "pwd", "prints the working directory of OpenSIPS",
                mi_pwd,        MI_NO_INPUT_FLAG,  0,  0 },

... ... ... /* For exporting the populated array of MI functions Parameters :

      mod_name : the name of the module exporting these functions
      mis : the array of exported MI functions

Returns :

      0 on success, negative in case of error
  • /

int register_mi_mod( char *mod_name, mi_export_t *mis);

/* Example of usage */ if (register_mi_mod( "core", mi_core_cmds)<0) {

      LM_ERR("unable to register core MI cmds\n");
      return -1;  
to:

struct mi_root *mi_debug(struct mi_root *cmd, void *param) {

      struct mi_root *rpl_tree;
      struct mi_node *node;
      char *p;
      int len;
      int new_debug;

      /* check the kids member of our root node - 
      if the input root node has kids, our command was called with parameters */
      node = cmd->node.kids;
      if (node!=NULL) {
            /* take the node's value and convert it to int, to make sure the parameter is valid */
            if (str2sint( &node->value, &new_debug) < 0)
                  /* if failed to convert to int, still return a RPL tree with an >=400 code and reason */
                  return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
      } else
            new_debug = *debug;

      /* all is good so far, initialize a new output ROOT tree which has a 200 OK code & reason */
      rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
      if (rpl_tree==0)
              return 0;

      p = sint2str((long)new_debug, &len);
      /* add a new node to our output tree, which the current debug level */
      node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE,
             MI_SSTR("DEBUG"),p, len);
      if (node==0) {
              free_mi_tree(rpl_tree);
              return 0;
      }

      /* if all was successful, overwrite the actual debug level, and return our tree */
      *debug = new_debug;
      return rpl_tree;
Changed lines 1427-1441 from:

The structures commonly used for implementing MI functions are also found in mi/mi.h :

to:

For more generic information on the MI Interface as well as some examples used for running MI commands with the opensipsctl utility, see the MI Interface documentation page.

Statistics API

OpenSIPS exposes a statistics API that can be used both from the core or the modules. The statistics are essentially counters that will be internally incremented/decremented by OpenSIPS and that can be fetched by the outside world ( via the MI interface ) for understanding the OpenSIPS load / health status / etc.
The advantages of using the OpenSIPS Statistics API instead of regular counters is :

  • easily fetched from the MI Interface
  • on supported architectures, the statistics do not use an explicit lock ( the consistency is ensured by employing assembly code ), thus you will get better performance


The most important structures used for extending statistics are exported by statistics.h :

Changed lines 1443-1478 from:

/* Parameters :

      input : the tree that contains the command paramenters
      param : the parameter provided at function registration

Returns :

      A mi_root tree containing the function reply
  • /

typedef struct mi_root* (mi_cmd_f)(struct mi_root *input, void *param);

/* below are the used structures for representing the tree root and the tree nodes */ struct mi_root {

      /* int code - similar to SIP or HTTP code */
      unsigned int       code;
      /* string reason for code - similar to SIP or HTTP reason */
      str                reason;
      /* handler in case of asynchronous MI commands */ 
      struct mi_handler  *async_hdl;
      /* the actual root node in our tree */
      struct mi_node     node; 

};

struct mi_node {

      str value;
      str name;
      unsigned int flags;
      struct mi_node *kids;
      struct mi_node *next;
      struct mi_node *last; 
      struct mi_attr *attributes;

};

struct mi_attr{

        str name;
        str value;
        struct mi_attr *next;

};

to:

typedef struct stat_export_ {

        char* name;                /* null terminated statistic name */
        unsigned short flags;      /* flags */
        stat_var** stat_pointer;   /* pointer to the variable's mem location *
                                    * NOTE - it's in shm mem */

} stat_export_t;

Changed lines 1451-1453 from:

As can be noted from the above tree definition, a node has a name and a value associated to it,it can have one or multiple children ( stored in kids pointer ), and also it can have a list of key-value attributes associated to it.
For building the output MI tree, mi/tree.h and mi/attr.h expose the following functions :

to:

For example, the core stats exported by OpenSIPS are defined in the following array :

Changed lines 1454-1492 from:

/* Use for creating a new output reply tree

to:

stat_var* rcv_reqs; stat_var* rcv_rpls; stat_var* fwd_reqs; stat_var* fwd_rpls; stat_var* drp_reqs; stat_var* drp_rpls; stat_var* err_reqs; stat_var* err_rpls; stat_var* bad_URIs; stat_var* unsupported_methods; stat_var* bad_msg_hdr;

stat_export_t core_stats[] = {

        {"rcv_requests" ,         0,  &rcv_reqs              },
        {"rcv_replies" ,          0,  &rcv_rpls              },
        {"fwd_requests" ,         0,  &fwd_reqs              },
        {"fwd_replies" ,          0,  &fwd_rpls              },
        {"drop_requests" ,        0,  &drp_reqs              },
        {"drop_replies" ,         0,  &drp_rpls              },
        {"err_requests" ,         0,  &err_reqs              },
        {"err_replies" ,          0,  &err_rpls              },
        {"bad_URIs_rcvd",         0,  &bad_URIs              },
        {"unsupported_methods",   0,  &unsupported_methods   },
        {"bad_msg_hdr",           0,  &bad_msg_hdr           },
        {"timestamp",  STAT_IS_FUNC, (stat_var**)get_ticks   },
        {0,0,0}

}; (:sourceend:)

As note from the above structure, statistics can either be a simple counter ( eg. rcv_requests ), but it can also be a function. Statistics function might come in handy when the developer needs to do extra processing on the raw counters before providing the final output.


After defining your array of statistics that you want to export, one should use the following for exporting the stats to be accessible by all

(:source lang=C -link -getcode :) /*

Changed lines 1494-1496 from:
      code : success code for this tree ( >=200<300 for success, anything else for errors )
      reason : string reasons representation for the code
      reason_len : length of the reason parameter
to:
      module - a string describing the module the current statistics belong to. Will be used when fetching the statistics via MI
      stats - the statistics to be registered
Changed lines 1497-1509 from:
      A new mi_root tree, or NULL in case of error. Note that this function will allocate the node in PKG and it typically has to be returned - the freeing will be done in the MI core, after the output tree is written by the transport module */

struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len);

/* Adding a new child node to our tree - typically first called to mi_root->node.kids Parameters :

      parent : the parent node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
to:
      0 in case of success, negative in case of error
Changed lines 1499-1501 from:

struct mi_node *add_mi_node_child(struct mi_node *parent, int flags,

        char *name, int name_len, char *value, int value_len);

/* Adding a new sibling node to one of our nodes

to:

int register_module_stats(char *module, stat_export_t *stats; (:sourceend:)
Note that register_module_stats will export the statistics, and also allocate them in SHM memory, for them to be accessible by all OpenSIPS processes.

Important to note here that all the above statistics related functions MUST be called in the context of the attendant process before forking is done.


At runtime, the developer has access to the following functions for operating on statistics :

(:source lang=C -link -getcode :) /*

Changed lines 1515-1522 from:
      brother : the brother node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
to:
      var : the statistics to be updated
      n : the value ( if positive -> stat will be increment. negative -> stat will be decremented )
Changed lines 1518-1528 from:

struct mi_node *add_mi_node_sibling(struct mi_node *brother, int flags,

        char *name, int name_len, char *value, int value_len);

/* Adding a new attribute to one of our nodes

      node : the node we will be adding the key-value attribute to
      flags : Current options are :
                   MI_DUP_NAME : the name of this attribute needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current attribute needs to be duplicated in PKG
      name : the name of the current attribute
      name_len : length of the node's attribute name
      value : the value of the current value
      value_len : length of the node's attribute value
to:

void update_stat(stat_var* var, int n);

/* Parameters :

      var : the statistics to be reseted
Changed lines 1524-1525 from:

struct mi_attr *add_mi_attr(struct mi_node *node, int flags,

        char *name, int name_len, char *value, int value_len)
to:

void reset_stat(stat_var* var);

/* Parameters :

      var : the statistics to be fetched

Returns :

      statistic value
  • /

unsigned long get_stat_val(stat_var* var)

Changed lines 1534-1578 from:


Further on, we will follow the implementation of the debug MI function. If called with no parameters, the function will return the current debug level in OpenSIPS. If called with one integer parameter, then the function will set the current debug level to the provided parameter.

(:source lang=C -link -getcode :) struct mi_root *mi_debug(struct mi_root *cmd, void *param) {

      struct mi_root *rpl_tree;
      struct mi_node *node;
      char *p;
      int len;
      int new_debug;

      /* check the kids member of our root node - 
      if the input root node has kids, our command was called with parameters */
      node = cmd->node.kids;
      if (node!=NULL) {
            /* take the node's value and convert it to int, to make sure the parameter is valid */
            if (str2sint( &node->value, &new_debug) < 0)
                  /* if failed to convert to int, still return a RPL tree with an >=400 code and reason */
                  return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
      } else
            new_debug = *debug;

      /* all is good so far, initialize a new output ROOT tree which has a 200 OK code & reason */
      rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
      if (rpl_tree==0)
              return 0;

      p = sint2str((long)new_debug, &len);
      /* add a new node to our output tree, which the current debug level */
      node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE,
             MI_SSTR("DEBUG"),p, len);
      if (node==0) {
              free_mi_tree(rpl_tree);
              return 0;
      }

      /* if all was successful, overwrite the actual debug level, and return our tree */
      *debug = new_debug;
      return rpl_tree;

} (:sourceend:)
>>tip<< For more generic information on the MI Interface as well as some examples used for running MI commands with the opensipsctl utility, see the MI Interface documentation page.

to:

All statistics related code should be guarded by #ifdef STATISTICS , since the statistics are not a mandatory part of the OpenSIPS core ( they can be disabled from within menuconfig ).

June 15, 2014, at 04:29 PM by 188.27.106.202 -
Added lines 1533-1536:


>>tip<< For more generic information on the MI Interface as well as some examples used for running MI commands with the opensipsctl utility, see the MI Interface documentation page.

June 15, 2014, at 04:26 PM by 188.27.106.202 -
Added lines 2543-2546:

Adding new module pseudo-variables is done by populating the items member in our module's exports structure.
For the full description of the used structures and functions for pseudo-variables, please see the generic Pseudovariables section.

June 15, 2014, at 04:22 PM by 188.27.106.202 -
Changed lines 2524-2525 from:

For the full description of the used structures and functions, please see the generic Management Interface API section.

to:

For the full description of the used structures and functions, please see the generic Management Interface API section.

The MI functions in the mi_cmds member of the exports structure will be automatically registered by the module interface.

Added lines 2531-2540:

Adding new module exported statistics is done by populating the stats member in our module's exports structure.
For the full description of the used structures and functions for statistics, please see the generic Statistics API section.

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 :
opensipsctl fifo get_statistics mynewmod mycustomstat

June 15, 2014, at 04:08 PM by 188.27.106.202 -
Changed line 2524 from:

For the full description of the used structures and functions, please see the generic section.

to:

For the full description of the used structures and functions, please see the generic Management Interface API section.

June 15, 2014, at 04:06 PM by 188.27.106.202 -
Changed line 2524 from:

For a description of the used structures and functions, please see the generic section.

to:

For the full description of the used structures and functions, please see the generic section.

June 15, 2014, at 04:05 PM by 188.27.106.202 -
Changed line 2524 from:

For a description of the used structures and functions, please see the generic http://www.opensips.org/Documentation/Development-Manual#toc27? section.

to:

For a description of the used structures and functions, please see the generic section.

June 15, 2014, at 04:04 PM by 188.27.106.202 -
Added lines 2521-2524:

Adding new module MI functions is done by populating the mi_cmds member in our module's exports structure.
For a description of the used structures and functions, please see the generic http://www.opensips.org/Documentation/Development-Manual#toc27? section.

June 15, 2014, at 03:41 PM by 188.27.106.202 -
Changed line 2584 from:

Useful Module APIs

to:

Module APIs

June 15, 2014, at 03:41 PM by 188.27.106.202 -
Changed lines 2582-2590 from:

The flags member of the structure can be 0 or PROC_FLAG_INITCHILD. If PROC_FLAG_INITCHILD is provided, all the child_init function from the loaded modules will also be run in the context of our new module process.

to:

The flags member of the structure can be 0 or PROC_FLAG_INITCHILD. If PROC_FLAG_INITCHILD is provided, all the child_init function from the loaded modules will also be run in the context of our new module process.

Useful Module APIs

TM module
RR Module
Dialog Module
June 02, 2014, at 05:21 PM by 89.120.101.121 -
Changed line 2549 from:

Once the function exists, the entire OpenSIPS will stop.

to:

Once the function exits, the entire OpenSIPS will stop.

June 02, 2014, at 05:18 PM by 89.120.101.121 -
Added line 2546:
Added line 2551:
Added line 2555:
Added line 2559:
Added line 2563:
Added line 2567:
June 02, 2014, at 05:17 PM by 89.120.101.121 -
Changed lines 2526-2576 from:
Adding module dedicated Processes
to:
Adding module dedicated Processes

For certain use cases, our module might need to talk to external entities which are not SIP based.
For such cases, we will need to have one ( or multiple ) processes which will be dedicated to handling such communication. Examples for this include the RTPProxy module ( which handles the communication with an external RTP Proxy engine ) or even the mi_fifo and mi_datagram ( which read MI commands from a FIFO file or respectively an UDP socket ).
Adding new module parameters is done by populating the procs member in our module's exports structure. The proc_export_t structure describing extra requested processes is the following : (:source lang=C -link -getcode :) 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)();

(:sourceend:)
>>important<< The function that will run in the context of the new process must never terminate. Once the function exists, the entire OpenSIPS will stop.


The pre_fork_function and post_fork_function serve as helpers to create various auxiliary needed by the starting of the main process.
>>important<< They are both executed within the context of the attendant OpenSIPS process


The no member of the structure dictates how many processes OpenSIPS will fork in order to run the respective function.
It can come in handy when there is a big work-load to be handled, at your module logic will spread the work load across all the forked processes, by making use of the no parameter provided to the process function.
>>tip<< The number of processes forked by OpenSIPS for a particular function is not neccesarily static.


Below is an example of how the MI datagram handles the forking of processes. The default value for the number of processes is MI_CHILD_NO, but that number is also configurable by the children_count parameter, as seen below. (:source lang=C -link -getcode :) 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           },

(:sourceend:)
The flags member of the structure can be 0 or PROC_FLAG_INITCHILD. If PROC_FLAG_INITCHILD is provided, all the child_init function from the loaded modules will also be run in the context of our new module process.

June 02, 2014, at 12:26 PM by 89.120.101.121 -
Changed lines 1053-1101 from:

inline static rw_lock_t * lock_init_rw(void)

to:

inline static rw_lock_t * lock_init_rw(void); (:sourceend:)
In order to acquire the lock for reading purpose, one should use lock_start_read : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
  • /

void lock_start_read(rw_lock_t * lock); (:sourceend:)
In case there is currently a write access ongoing, the lock will block until the write is done. Otherwise, the lock will be acquired immediately.
After the read is finished, you must call lock_stop_read : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be released
  • /

void lock_stop_read(rw_lock_t * lock); (:sourceend:)
For requesting a write access, you can use lock_start_write : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
  • /

void lock_start_write(rw_lock_t * lock); (:sourceend:)
In case there are other ongoing writes, the op will block until the other writes complete. Otherwise, the lock will block until all the existing readers finish reading.
After the write operation is finished, call lock_stop_write : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be release
  • /

void lock_stop_write(rw_lock_t * lock); (:sourceend:)
Upon calling the above function, readers will again be allowed in the critical section.
For destroying and deallocating a rw lock, use lock_destroy_rw : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be destroyed
  • /

void lock_destroy_rw(rw_lock_t * lock);

June 02, 2014, at 12:10 PM by 89.120.101.121 -
Added lines 1041-1054:

A readers-writer lock is like a mutex, in that it controls access to a shared resource, allowing concurrent access to multiple threads for reading but restricting access to a single thread for writes (or other changes) to the resource.
This can prove very useful when having an use case where all the OpenSIPS processes need read-only access to a resource, but you need to have an MI command to reload that resource ( eg. from a database ). In such scenarios, using a readers-writers lock can improve performance by a considerable margin.
The API can be used by including “rw_locking.h” . The OpenSIPS generic lock is defined by the rw_lock_t structure.

Allocating a new readers-writers lock into shared memory and initializing it is done by calling lock_init_rw :

(:source lang=C -link -getcode :) /* Returns :

      A shared memory allocated rw lock, or NULL in case of an error.
  • /

inline static rw_lock_t * lock_init_rw(void) (:sourceend:)

June 01, 2014, at 11:42 PM by 188.27.107.106 -
Added lines 1039-1040:
Readers-Writers Locking API
June 01, 2014, at 11:04 PM by 188.27.107.106 -
Added lines 2432-2454:

(:source lang=C -link -getcode :) 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);

(:sourceend:)
As we can see, the input char* parameters are casted to their according valus after fixup.
Again, mod_fix.h provides varius functions for accessing the results of the fixup.In our example, fixup_get_ivalue is used to get the provided integer value ( either directly from the plaintext provided integer, or it will extract the integer value from the spec that was parsed at fixup ). Also, note how the active parameter is cast directly to long, since we're accepting only plain-text integers for that parameter, and it has been already converted for us at fixup time.

The return code of the script exported functions from the module are very important.
A strictly positive return code will mean success, while a stricly negative return code will signal a failure
Returning 0 in a function exporting to the script will STOP the script execution after the function ends. Use a 0 return code with caution and only when absolutely necessary.

June 01, 2014, at 11:02 PM by 188.27.107.106 -
Changed lines 2384-2385 from:
  • if we accept it, our functions can accept pseudo-variables into the provided parameters. In this case, the fixup can be used to lookup the pvar's spec, and then at runtime we will be left just with evaluating that specific pvar.
to:
  • if we accept it, our functions can accept pseudo-variables into the provided parameters. In this case, the fixup can be used to lookup the pvar's spec, and then at runtime we will be left just with evaluating that specific pvar in the context of the current SIP message


Further on we will follow the implementation of lb_is_destination from the load_balancer module, to fully grasp the concept. The function definition is the following : (:source lang=C -link -getcode :)

        {"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:)
As noted, the function receives 4 parameters. The desired usage case for lb_is_destination(ip,port,group,active) is the following :

  • ip - string or pvar with the IP to check
  • port - string or pvar with the Port to check - if empty, we skip any port check
  • group - integer or pvar containing integer with the load_balancer group to check
  • active - integer. if 1, we accept just active destinations for our check


Knowing these, the fixup_is_dst is the following : (:source lang=C -link -getcode :) 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;
        }

} (:sourceend:)
The fixup function will get called for each parameter provided, with the param_no parameter representing the index of the parameter we are parsing ( starting with 1, since the first parameter is the actual SIP msg )
mod_fix.h exports many helper functions that can be used for fixups. See the file for a full list of currently implemented fixups.
The above fixup functions will replace the parameter that you will receive in the main function with their respective output. Thus, in the main function you will not receive any more the plain text parameters that were provided by the script writer, but rather you'll receive the pvar's spec after it was parsed, or directly the integer value supplied by the script writer. Here is how the w_lb_is_dst4 handles the provided parameters after fixup :

June 01, 2014, at 10:59 PM by 188.27.107.106 -
Changed line 2358 from:

As can be seen, all the OpenSIPS module functions receive string parameters ONLY, and a function can have a maximum of 6 parameters. The SIP message that is currently being processed is also transmitted as the first parameter, although this is transparent to the script writes ( he just provides the parameters idx 1 to 5 )

to:

As can be seen, all the OpenSIPS module functions receive string parameters ONLY, and a function can have a maximum of 6 parameters. The SIP message that is currently being processed is also transmitted as the first parameter, although this is transparent to the script writer ( he just provides the parameters idx 1 to 5 )

Added lines 2380-2385:


A very important concept to grasp here is the fixup_function. This function is called just once, when the script is initially parsed, and it serves as an optimization, where the provided parameters are further parsed so that we can speed up the runtime function.
Examples for the use cases of the fixup function, just to provide a few :

  • since all our module parameters are strings, sometimes our module might need an integer parameter to be passed in that string. The fixup function can be used to convert that string to integer only once
  • if we accept it, our functions can accept pseudo-variables into the provided parameters. In this case, the fixup can be used to lookup the pvar's spec, and then at runtime we will be left just with evaluating that specific pvar.
June 01, 2014, at 10:57 PM by 188.27.107.106 -
Added lines 2331-2379:

Adding new module parameters is done by populating the cmds member in our module's exports structure.
The exported functions structure is the following : (:source lang=C -link -getcode :) 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 */

}; (:sourceend:)
Very similar to the params member in the exports structure, the cmds member MUST be NULL terminated.
At startup, OpenSIPS tries to locate each function called in the script either in the core functions, or in the list of functions exported by all list modules.

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 :) As can be seen, all the OpenSIPS module functions receive string parameters ONLY, and a function can have a maximum of 6 parameters. The SIP message that is currently being processed is also transmitted as the first parameter, although this is transparent to the script writes ( he just provides the parameters idx 1 to 5 ) (: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 :)

  1. define REQUEST_ROUTE 1 /*!< Request route block */
  2. define FAILURE_ROUTE 2 /*!< Negative-reply route block */
  3. define ONREPLY_ROUTE 4 /*!< Received-reply route block */
  4. define BRANCH_ROUTE 8 /*!< Sending-branch route block */
  5. define ERROR_ROUTE 16 /*!< Error-handling route block */
  6. define LOCAL_ROUTE 32 /*!< Local-requests route block */
  7. define STARTUP_ROUTE 64 /*!< Startup route block */
  8. define TIMER_ROUTE 128 /*!< Timer route block */
  9. define EVENT_ROUTE 256 /*!< Event route block */

(:sourceend:)
Allowing multiple types of routes by provided a bitmask of the above values is also supported.

June 01, 2014, at 06:20 PM by 213.233.84.72 -
Changed line 2280 from:
Adding module parameters
to:
Adding module Parameters
Changed lines 2328-2338 from:

(:sourceend:)

to:

(:sourceend:)

Adding module Functions
Adding module MI Functions
Adding module Statistics
Adding module Pseudo-variables
Adding module dedicated Processes
June 01, 2014, at 06:14 PM by 213.233.84.72 -
Changed line 2325 from:
        LM_INFO("Our parameter has been set : value is %d\n",(char *)val);
to:
        LM_INFO("Our parameter has been set : value is %s\n",(char *)val);
June 01, 2014, at 06:12 PM by 213.233.84.72 -
Added lines 2281-2328:

Adding 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:)
The OpenSIPS modules can export both string and integer parameters.
Find below some examples for each of them. Note how the param_export_t structure does not receive any length parameters as to indicate how many parameters the module exports - rather, the structure must end with a row full of 0,

(: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:)
Example of setting these parameters from the OpenSIPS script, for our ournewmod module. (:source lang=C -link -getcode :) loadmodule "ournewmod.so"

modparam("ournewmod","enable_stats", 1) modparam("ournewmod","db_url","mysql://vlad:mypw@localhost/opensips") (:sourceend:)
Also, OpenSIPS supports triggering a module's internal function when the script writer set a particular parameter. This can prove useful if the provided parameter needs to be converted to a form that the module knows how to process, or simply if one parameter should be able to be set multiple times.
Find an example of such parameters below, where an NoSQL URL can be set multiple times in order to initialize as many back-end connections :

(: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 %d\n",(char *)val);
        /* continue processing, eg : add our new parameter to a list to be further processed */

} (:sourceend:)

June 01, 2014, at 06:08 PM by 213.233.84.72 -
Added lines 2270-2278:

This function must be specified in the destroy_function member of our module_exports exports structure.
It is ran from within a single process' context ( the attendant ), when OpenSIPS is about to shutdown.
The purpose of the function is to cleanup various resources that OpenSIPS has been using ( shared memory, DB connections, etc ). Also, the destroy_function is a good time to save whatever state that the module was keeping into a persistent storage, so that they can be loaded afterwards, when OpenSIPS starts. ( eg. the dialog module saves all the dialog states in the database in the destroy function ).
Prototype of the function is

(:source lang=C -link -getcode :) typedef void (*destroy_function)(); (:sourceend:)

June 01, 2014, at 06:04 PM by 213.233.84.72 -
Added line 2262:
June 01, 2014, at 06:03 PM by 213.233.84.72 -
Changed lines 2229-2230 from:
to:


Added lines 2237-2266:

This function must be specified in the init_child_f member of our module_exports exports structure.
It is ran from within the context of ALL OpenSIPS processes, right after the new processes has been forked.
The purpose of the function is to create various connectors ( db, cachedb, etc ) which should be different for each created OpenSIPS process and to initialize various other variables depending on the OpenSIPS process context we are currently in.
Prototype of the function is

(: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:)
The function will receive an integer parameter, indicating the type of OpenSIPS process that is currently running our function. Below are all the available options :

(:source lang=C -link -getcode :)

  1. define PROC_MAIN 0 /* Main opensips process */
  2. define PROC_TIMER -1 /* Timer attendant process */
  3. define PROC_MODULE -2 /* Extra process requested by modules */
  4. define PROC_TCP_MAIN -4 /* TCP main process */
  5. define PROC_BIN -8 /* Any binary interface listener */

(:sourceend:)
A positive value for our rank parameter denotes that we are currently operating in the context of an OpenSIPS listener ( UDP, TCP or SCTP ).
>>tip<< 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.
This will make OpenSIPS startup faster, and also we will be able to process traffic faster ( at least the traffic that does not explicitly depend on having our module's internal data fully populated ).

June 01, 2014, at 05:59 PM by 213.233.84.72 -
Added lines 2218-2233:

This function must be specified in the init_f member of our module_exports exports structure.
It is ran from within a single process' context ( the attendant ), after the full OpenSIPS config has been parsed ( our own module parameters included ), and all the helper APIs are initialized at this point ( shared memory, locking, timer processes, etc ).
The purpose of the function is to check the integrity of how the module was configured from the OpenSIPS script, to initialize needed structures, etc. Also, some critical resources ( like new timer processes, see above section ) can ONLY be initialized from the mod_init() function of our new modules.
Prototype of the function is

(: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.
Due to this, do NOT use the mod_init function to initialize structures / connections that should have different instances for each OpenSIPS process.

June 01, 2014, at 05:57 PM by 213.233.84.72 -
Added line 2200:
Added line 2204:
June 01, 2014, at 05:56 PM by 213.233.84.72 -
Added lines 2102-2103:
Introduction
Changed lines 2167-2221 from:

(:sourceend:)

to:

(:sourceend:)

Compiling a module

Further on, we will be following the various options we have in building our new module, named ournewmod.
After creating the ournewmod folder in the OpenSIPS modules/ path, we should create a Makefile for our module, located also in the ournewmod folder. The most basic Makefile for a module with no external library dependencies is the following :

(:source lang=C -link -getcode :)

  1. $Id$
  2. 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 (:sourceend:)
If the module has external library dependencies, they should be linked in the module's Makefile as well. Eg, the cachedb_memcached module : (:source lang=C -link -getcode :) include ../../Makefile.defs auto_gen= NAME=cachedb_memcached.so DEFS+=-I$(LOCALBASE)/include LIBS=-L$(LOCALBASE)/lib -lmemcached

include ../../Makefile.modules (:sourceend:)
>>important<< 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.
We should add a new line to Makefile.conf.template, with the following format : (:source lang=C -link -getcode :) modulename= Module Description | module dependency (:sourceend:)
Also, we should modify Makefile.conf.template to add our new module's name to the exclude_modules list of modules that will not compile by default.

Initializing the module

In the context of initializing our new module, there are two types of functions that will help us :

mod_init
child_init
Destroying the module
Adding module parameters
June 01, 2014, at 05:51 PM by 213.233.84.72 -
Changed lines 2100-2165 from:

Module Development

to:

Module Development

Due 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.
An OpenSIPS module is actually a shared library ( .so file ) which OpenSIPS can dinamically load at OpenSIPS startup, if the module is loaded from within the OpenSIPS script, by using the loadmodule directive :

(:source lang=C -link -getcode :) loadmodule "mynewmod.so" (:sourceend:)


Upon loading a new module, the OpenSIPS core will lookup the exports variable, of type struct module_exports. This structure and variable are of the utmost importance when developing a new OpenSIPS module

(: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:)
The module_exports contents ( along with the above coments ) are self-explanatory.
Further on, we will discuss about each member of the module_exports structure and how it is meant to be used when building a nw OpenSIPS. Purely as an example, See below the exports used by the dialog module (:source lang=C -link -getcode :)

        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:)

June 01, 2014, at 02:51 PM by 79.112.219.219 -
Added lines 1890-1891:
Changed lines 1893-1894 from:

For such scenarios, using the fetch_result API function is highly recommended :

to:

For such scenarios, using the fetch_result API function is highly recommended

May 31, 2014, at 10:12 PM by 79.112.219.219 -
Changed lines 2093-2095 from:

Event Interface

BIN Interface

to:

Event Interface API

BIN Interface API

May 31, 2014, at 10:12 PM by 79.112.219.219 -
Added lines 2092-2095:

Event Interface

BIN Interface

May 31, 2014, at 10:08 PM by 79.112.219.219 -
Added line 2072:

For running all other database queries, one should use the raw_query API function.

Deleted line 2073:

For running all other database queries, one should use the raw_query API function.

May 31, 2014, at 10:07 PM by 79.112.219.219 -
Added lines 2036-2089:

(:sourceend:)
Sometimes, for optimizing database operations, it is useful to know the auto-increment primary key value upon inserting / updating a record. For such cases, the last_inserted_id API call can be used : (:source lang=C -link -getcode :) /**

 * \brief Retrieve the last inserted ID in a table.
 *
 * The function returns the value generated for an AUTO_INCREMENT column by the
 * previous INSERT or UPDATE  statement. Use this function after you have
 * performed an INSERT statement into a table that contains an AUTO_INCREMENT
 * field.
 * \param _h structure representing database connection
 * \return returns the ID as integer or returns 0 if the previous statement
 * does not use an AUTO_INCREMENT value.
 */

typedef int (*db_last_inserted_id_f) (const db_con_t* _h); (:sourceend:)
Also, when we want to insert a row into a table, and update the row in case of duplicate key errors, we should use the insert_update API call : (:source lang=C -link -getcode :) /**

 * \brief Insert a row into specified table, update on duplicate key.
 *
 * The function implements the INSERT ON DUPLICATE KEY UPDATE SQL directive.
 * It is possible to insert a row and update if one already exists.
 * The old row will not deleted before the insertion of the new data.
 * \param _h structure representing database connection
 * \param _k key names
 * \param _v values of the keys
 * \param _n number of key=value pairs
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_insert_update_f) (const db_con_t* _h, const db_key_t* _k,

                                const db_val_t* _v, const int _n);

(:sourceend:)
(:source lang=C -link -getcode :) For running all other database queries, one should use the raw_query API function. /**

 * \brief Raw SQL query.
 *
 * This function can be used to do database specific queries. Please
 * use this function only if needed, as this creates portability issues
 * for the different databases. Also keep in mind that you need to
 * escape all external data sources that you use. You could use the
 * escape_common and unescape_common functions in the core for this task.
 * \see escape_common
 * \see unescape_common
 * \param _h structure representing database connection
 * \param _s the SQL query
 * \param _r structure for the result
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_raw_query_f) (const db_con_t* _h, const str* _s, db_res_t** _r);

May 31, 2014, at 09:57 PM by 79.112.219.219 -
Added lines 1996-2035:

(:sourceend:)
Updating rows in a table can be done by calling the update API function : (:source lang=C -link -getcode :) /**

 * \brief Update some rows in the specified table.
 *
 * The function implements UPDATE SQL directive. It is possible to modify one
 * or more rows in a table using this function.
 * \param _h database connection handle
 * \param _k array of keys (column names) that will be matched
 * \param _o array of operators to be used with key-value pairs
 * \param _v array of values that the row must match to be modified
 * \param _uk array of keys (column names) that will be modified
 * \param _uv new values for keys specified in _k parameter
 * \param _n number of key-value pairs in _k and _v parameters
 * \param _un number of key-value pairs in _uk and _uv parameters
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_update_f) (const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,

                                const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv,
                                const int _n, const int _un);

(:sourceend:)
Replacing rows in a database table can be done with the replace function from the API : (:source lang=C -link -getcode :) /**

 * \brief Insert a row and replace if one already exists.
 *
 * The function implements the REPLACE SQL directive. It is possible to insert
 * a row and replace if one already exists. The old row will be deleted before
 * the insertion of the new data.
 * \param _h structure representing database connection
 * \param _k key names
 * \param _v values of the keys
 * \param _n number of key=value pairs
 * \return returns 0 if everything is OK, otherwise returns value < 0
  • /

typedef int (*db_replace_f) (const db_con_t* handle, const db_key_t* keys,

                                const db_val_t* vals, const int n);
May 31, 2014, at 09:32 PM by 79.112.219.219 -
Deleted line 1955:

(:source lang=C -link -getcode :)

Added lines 1957-1958:

(:source lang=C -link -getcode :)

May 31, 2014, at 09:31 PM by 79.112.219.219 -
Added lines 1954-1994:

(:sourceend:)
(:source lang=C -link -getcode :) Inserting rows in a table can be done by calling the insert API function : /**

 * \brief Insert a row into the specified table.
 *
 * This function implements INSERT SQL directive, you can insert one or more
 * rows in a table using this function.
 * \param _h database connection handle
 * \param _k array of keys (column names)
 * \param _v array of values for keys specified in _k parameter
 * \param _n number of keys-value pairs int _k and _v parameters
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_insert_f) (const db_con_t* _h, const db_key_t* _k,

                                const db_val_t* _v, const int _n);

(:sourceend:)
Deleting rows from a table is accomplished by calling the delete API function :

(:source lang=C -link -getcode :) /**

 * \brief Delete a row from the specified table.
 *
 * This function implements DELETE SQL directive, it is possible to delete one or
 * more rows from a table.
 * If _k is NULL and _v is NULL and _n is zero, all rows are deleted, the
 * resulting table will be empty.
 * If _o is NULL, the equal operator "=" will be used for the comparison.
 *
 * \param _h database connection handle
 * \param _k array of keys (column names) that will be matched
 * \param _o array of operators to be used with key-value pairs
 * \param _v array of values that the row must match to be deleted
 * \param _n number of keys-value parameters in _k and _v parameters
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_delete_f) (const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,

                                const db_val_t* _v, const int _n);
May 31, 2014, at 09:18 PM by 79.112.219.219 -
Added lines 1910-1953:
        /* check if our used DB driver supports fetching a limited number of rows */
        if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
                /* run our query as usual, but DO NOT provide a result set pointer ( last parameter 0 ) */
                if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, db_cols, 0, 0 ) < 0) {
                        LM_ERR("DB query failed\n");
                        goto error;
                }
                /* estimate how many rows we can fit into our current PKG memory */
                no_rows = estimate_available_rows( 4+32+15+4+32+4+128+4+32+4, db_cols);
                if (no_rows==0) no_rows = 10;
                /* try to fetch our rows */
                if(dr_dbf->fetch_result(db_hdl, &res, no_rows )<0) {
                        LM_ERR("Error fetching rows\n");
                        goto error;
                }
        } else {
                /* no fetching rows support - fallback to full rows loading */
                if ( dr_dbf->query(db_hdl,0,0,0,columns,0,db_cols,0,&res) < 0) {
                        LM_ERR("DB query failed\n");
                        goto error;
                }
        }

        do {
                for(i=0; i < RES_ROW_N(res); i++) {
                        row = RES_ROWS(res) + i;
                        /* start processing our loaded rows */
                }

                if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
                        /* any more rows to fetch ? */
                        if(dr_dbf->fetch_result(db_hdl, &res, no_rows)<0) {
                                LM_ERR( "fetching rows (1)\n");
                                goto error;
                        }
                        /* success in fetching more rows - continue the loop */
                } else {
                        /* we were not supporting fetching rows in the first place, processed everything */
                        break;
                }
        } while(RES_ROW_N(res)>0);

        dr_dbf->free_result(db_hdl, res);
May 31, 2014, at 09:10 PM by 79.112.219.219 -
Added lines 1888-1909:

(:sourceend:)
Sometimes, especially when querying large tables, it is not desirable to fetch all the rows in one chunk, since that might lead to the filling of the OpenSIPS private memory.
For such scenarios, using the fetch_result API function is highly recommended :

(:source lang=C -link -getcode :) /**

 * \brief Fetch a number of rows from a result.                                 
 *
 * The function fetches a number of rows from a database result. If the number 
 * of wanted rows is zero, the function returns anything with a result of zero.
 * \param _h structure representing database connection
 * \param _r structure for the result
 * \param _n the number of rows that should be fetched            
 * \return returns 0 if everything is OK, otherwise returns value < 0         
 */

typedef int (*db_fetch_result_f) (const db_con_t* _h, db_res_t** _r, const int _n); (:sourceend:)
Find below a full example of using fetch_result :

(:source lang=C -link -getcode :)

May 31, 2014, at 09:03 PM by 79.112.219.219 -
May 31, 2014, at 09:03 PM by 79.112.219.219 -
Changed lines 1873-1888 from:
to:


As can be seen from the example above, any successful call to the query SQL function must be followed by the freeing of the returned result set, which is done by calling the free_result API function :

(:source lang=C -link -getcode :) /**

 * \brief Free a result allocated by db_query.
 *
 * This function frees all memory allocated previously in db_query. Its
 * neccessary to call this function on a db_res_t structure if you don't need the
 * structure anymore. You must call this function before you call db_query again!
 * \param _h database connection handle
 * \param _r pointer to db_res_t structure to destroy
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_free_result_f) (db_con_t* _h, db_res_t* _r); (:sourceend:)

May 31, 2014, at 09:00 PM by 79.112.219.219 -
Changed lines 1821-1823 from:
to:

db_row_t * rows; db_val_t * values;

Changed lines 1848-1852 from:

if (RES_ROW_N(db_res) <= 0) {

to:

nr_rows = RES_ROW_N(db_res); rows = RES_ROWS(res);

if (nr_rows <= 0) {

Added lines 1857-1870:

for (i=0;i<nr_rows;i++) {

      values = ROW_VALUES(rows + i);
      if (VAL_NULL(values)) {
            LM_WARN("Column value should not be null - skipping \n");
            continue;
      }

      LM_DBG("We have feteched %s\n",VAL_STRING(values));
      /* do further rows processing here */

}

sql_functions.free_result(db_handle, db_res); return 0;

April 22, 2014, at 08:09 PM by 213.233.85.84 -
Changed lines 1814-1851 from:
  1. TODO
to:

/* we will work on 'mytable' table with just two columns, keyname and value. The select query we will run is 'select value from mytable where keyname='abc';'

  • /

db_key_t key; db_val_t val; db_key_t col; db_res_t* db_res = NULL;

  1. define KEY_COL "keyname"
  2. define VALUE_COL "value"

str key_column = str_init(KEY_COL); str value_column = str_init(VALUE_COL); str db_table = str_init("mytable");

val.type = DB_STR; val.nul = 0; val.val.str_val.s = "abc"; val.val.str_val.len = 3;

key = &key_column; col = &value_column;

if (sql_functions.use_table(db_handle, &db_table) < 0) {

      LM_ERR("sql use_table failed\n");
      return -1;

}

if(sql_functions.query(db_handle, &key, NULL, &val, &col, 1, 1, NULL, &db_res) < 0) {

       LM_ERR("failed to query database\n");
       return -1;

}

if (RES_ROW_N(db_res) <= 0) {

      LM_DBG("no rows found\n");
      sql_functions.free_result(db_handle, db_res);
      return -1;

}

April 22, 2014, at 07:56 PM by 213.233.85.84 -
Added lines 1765-1815:

Many macros are in place in order to help writing faster and easier to read code :

(:source lang=C -link -getcode :) /* Macros below work on result sets ( db_res_t ) /** Return the column names */

  1. define RES_NAMES(re) ((re)->col.names)

/** Return the column types */

  1. define RES_TYPES(re) ((re)->col.types)

/** Return the number of columns */

  1. define RES_COL_N(re) ((re)->col.n)

/** Return the result rows */

  1. define RES_ROWS(re) ((re)->rows)

/** Return the number of current result rows */

  1. define RES_ROW_N(re) ((re)->n)

/** Return the last row of the result */

  1. define RES_LAST_ROW(re) ((re)->last_row)

/** Return the number of total result rows */

  1. define RES_NUM_ROWS(re) ((re)->res_rows)

/* Macros below work on rows */ /** Return the columns in the row */

  1. define ROW_VALUES(rw) ((rw)->values)

/** Return the number of colums */

  1. define ROW_N(rw) ((rw)->n)

/* Macros below work on values */ /**

 * Use this macro if you need to set/get the type of the value.
 */
  1. define VAL_TYPE(dv) ((dv)->type)

/**

 * Use this macro if you need to set/get the null flag. A non-zero flag means that
 * the corresponding cell in the database contains no data (a NULL value in MySQL
 * terminology).
 */
  1. define VAL_NULL(dv) ((dv)->nul)

/**

 * Use this macro if you need to access the integer value in the db_val_t structure.
 */
  1. define VAL_INT(dv) ((dv)->val.int_val)

/**

 * Use this macro if you need to access the str structure in the db_val_t structure.
 */
  1. define VAL_STR(dv) ((dv)->val.str_val)

(:sourceend:)
Find below an example of a full select query from start to finish :

(:source lang=C -link -getcode :)

  1. TODO

(:sourceend:)

April 22, 2014, at 07:48 PM by 213.233.84.113 -
Added lines 1659-1764:

For running a SELECT query, you should use the query function. Prototype is : (:source lang=C -link -getcode :) /**

 * \brief Query table for specified rows.
 *
 * This function implements the SELECT SQL directive.
 * If _k and _v parameters are NULL and _n is zero, you will get the whole table.
 *
 * if _c is NULL and _nc is zero, you will get all table columns in the result.
 * _r will point to a dynamically allocated structure, it is neccessary to call
 * db_free_result function once you are finished with the result.
 *
 * If _op is 0, equal (=) will be used for all key-value pairs comparisons.
 *
 * Strings in the result are not duplicated, they will be discarded if you call
 * db_free_result, make a copy yourself if you need to keep it after db_free_result.
 *
 * You must call db_free_result before you can call db_query again!
 * \see db_free_result
 *
 * \param _h database connection handle
 * \param _k array of column names that will be compared and their values must match
 * \param _op array of operators to be used with key-value pairs
 * \param _v array of values, columns specified in _k parameter must match these values
 * \param _c array of column names that you are interested in
 * \param _n number of key-value pairs to match in _k and _v parameters
 * \param _nc number of columns in _c parameter
 * \param _o order by statement for query
 * \param _r address of variable where pointer to the result will be stored
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_query_f) (const db_con_t* _h, const db_key_t* _k, const db_op_t* _op,

                                const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
                                const db_key_t _o, db_res_t** _r);

(:sourceend:)
Upon a successful select query call, the developer will have to manipulate the output db_res_t in order to use the results of his select query. Below are the used structures for interpreting a query result set.

(:source lang=C -link -getcode :) /**

 * This type represents a result returned by db_query function (see below). The
 * result can consist of zero or more rows (see db_row_t description).
 *
 * Note: A variable of type db_res_t returned by db_query function uses dynamicaly
 * allocated memory, don't forget to call db_free_result if you don't need the
 * variable anymore. You will encounter memory leaks if you fail to do this!
 *
 * In addition to zero or more rows, each db_res_t object contains also an array
 * of db_key_t objects. The objects represent keys (names of columns). *
 */

typedef struct db_res {

        struct {
                db_key_t* names;   /**< Column names                    */
                db_type_t* types;  /**< Column types                    */
                int n;             /**< Number of columns               */
        } col;
        struct db_row* rows;   /**< Rows                            */
        int n;                 /**< Number of rows in current fetch */
        int res_rows;          /**< Number of total rows in query   */
        int last_row;          /**< Last row                        */

} db_res_t;

/**

 * Structure holding the result of a query table function.
 * It represents one row in a database table. In other words, the row is an
 * array of db_val_t variables, where each db_val_t variable represents exactly
 * one cell in the table.
 */

typedef struct db_row {

        db_val_t* values;  /**< Columns in the row */
        int n;             /**< Number of columns in the row */

} db_row_t;

/**

 * This structure represents a value in the database. Several datatypes are
 * recognized and converted by the database API. These datatypes are automaticaly
 * recognized, converted from internal database representation and stored in the
 * variable of corresponding type.
 *
 * Module that want to use this values needs to copy them to another memory
 * location, because after the call to free_result there are not more available.
 *
 * If the structure holds a pointer to a string value that needs to be freed
 * because the module allocated new memory for it then the free flag must
 * be set to a non-zero value. A free flag of zero means that the string
 * data must be freed internally by the database driver.
 */

typedef struct {

        db_type_t type; /**< Type of the value                              */
        int nul;                /**< Means that the column in database has no value */
        int free;               /**< Means that the value should be freed */
        /** Column value structure that holds the actual data in a union.  */
        union {
                int           int_val;    /**< integer value              */
                long long     bigint_val; /**< big integer value          */
                double        double_val; /**< double value               */
                time_t        time_val;   /**< unix time_t value          */
                const char*   string_val; /**< zero terminated string     */
                str           str_val;    /**< str type string value      */
                str           blob_val;   /**< binary object data         */
                unsigned int  bitmap_val; /**< Bitmap data type           */
        } val;

} db_val_t; (:sourceend:) \\

April 22, 2014, at 07:30 PM by 213.233.84.113 -
Changed lines 1638-1658 from:
to:


Before running a query through the API, we need to choose the table that the query will run on :

(:source lang=C -link -getcode :) /**

 * \brief Specify table name that will be used for subsequent operations.
 *
 * The function db_use_table takes a table name and stores it db_con_t structure.
 * All subsequent operations (insert, delete, update, query) are performed on
 * that table.
 * \param _h database connection handle
 * \param _t table name
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */

typedef int (*db_use_table_f)(db_con_t* _h, const str * _t); (:sourceend:)

All queries must be preceded by a call to the use_table function. OpenSIPS internally does connection pooling - in case multiple module request connections to the same database, the connection will be shared between all those modules. Thus, in the context of a process, the same connection might be used by different modules - never assume a connection is dedicated to a single module.

April 22, 2014, at 07:23 PM by 213.233.84.113 -
Added lines 1608-1637:

The output of the init() function will be the handler to be further used for all database interactions. When the connection is not needed anymore, the close method should be called :

(:source lang=C -link -getcode :) /**

 * \brief Close a database connection and free all memory used.
 *
 * The function closes previously open connection and frees all previously
 * allocated memory. The function db_close must be the very last function called.
 * \param _h db_con_t structure representing the database connection
 */

typedef void (*db_close_f) (db_con_t* _h); (:sourceend:)
Before running any queries on the back-end, common practice dictates that the used tables should be versioned, in order to ensure that the user is not running your code on top of an older database structure.
db/db.h exposes db_check_table_version for this purpose, that checks the version table in the default OpenSIPS database structure :

(:source lang=C -link -getcode :) /* Parameters :

      dbf - the functions to be used for running the version query
      dbh - the connection to run the version query
      table - str containing the table name we want to check for version
      version - the version we expect to find

Returns :

      0 means table version was successfully validated, negative in case of error ( internal error or older version found )
 */

int db_check_table_version(db_func_t* dbf, db_con_t* dbh, const str* table, const unsigned int version); (:sourceend:)

April 22, 2014, at 07:13 PM by 213.233.84.113 -
Added lines 1568-1608:


Now that we have binded to the needed module and also made sure it supports our needed capabilities, we can go ahead and connect to the back-end, by invoking the init function from the binded functions : (:source lang=C -link -getcode :) /**

 * \brief Initialize database connection and obtain the connection handle.
 *
 * This function initialize the database API and open a new database
 * connection. This function must be called after db_bind_mod but before any
 * other database API function is called.
 *
 * The function takes one parameter, the parameter must contain the database
 * connection URL. The URL is of the form
 * mysql://username:password\@host:port/database where:
 *
 * username: Username to use when logging into database (optional).
 * password: password if it was set (optional)
 * host:     Hosname or IP address of the host where database server lives (mandatory)
 * port:     Port number of the server if the port differs from default value (optional)
 * database: If the database server supports multiple databases, you must specify the
 * name of the database (optional).
 * \see bind_dbmod
 * \param _sqlurl database connection URL
 * \return returns a pointer to the db_con_t representing the connection if it was
 * successful, otherwise 0 is returned
 */

typedef db_con_t* (*db_init_f) (const str* _sqlurl);

/* Example of usage below */ static db_con_t* db_connection;

if ((db_connection = sql_functions.init(db_url)) == NULL) {

      LM_ERR("Failed to connect to the database \n");
      return -1;

} (:sourceend:)

Connection sharing between multiple processes does not work for the majority of back-end specific connectors ( eg. MySQL, Postgres, etc ). Due to this fact, the developers MUST make sure to create a sepparate database connection for each process that will eventually need one - in the context of Module development, the connections need to be opened in the child_init function.

April 22, 2014, at 06:01 PM by 213.233.84.113 -
Changed lines 1476-1477 from:

db/db.h exposes most of the database related functions. At startup, the developer will have just the database URL where he needs to connect. By calling, db_bind_mod , the OpenSIPS DB API will try to automatically locate the actual DB module that support that specific back-end, and will return all the needed functions for operating on the back-end.

to:

db/db.h exposes most of the database related functions. At startup, the developer will have just the database URL where he needs to connect. By calling db_bind_mod , the OpenSIPS DB API will try to automatically locate the actual DB module that support that specific back-end, and will return all the needed functions for operating on the back-end.

Added lines 1517-1526:

/* Example of usage below */ db_func_t sql_functions; db_url = str_init("mysql://root:vlad@localhost/opensips");

if (db_bind_mod(db_url, &sql_functions) < 0){

      /* most likely the db_mysql modules was not loaded, or it was loaded after our module */
      LM_ERR("Unable to bind to a database driver\n");
      return -1;

}

Added lines 1528-1568:

After successfully binding to the module, the developer must also make sure that the URL provided from the script writer point of a back-end which also supports the capabilities that will be further used ( eg. when operating on a flat text file, the db_last_inserted_id_f function will not be populated, and thus if the C code calls that function, the module will crash ). This is done by using the DB_CAPABILITY macro :

(:source lang=C -link -getcode :) /**

 * Returns true if all the capabilities in cpv are supported by module
 * represented by dbf, false otherwise
 */
  1. define DB_CAPABILITY(dbf, cpv) (((dbf).cap & (cpv)) == (cpv))

/**

 * Represents the capabilities that a database driver supports.
 */

typedef enum db_cap {

        DB_CAP_QUERY =     1 << 0,  /**< driver can perform queries                                     */
        DB_CAP_RAW_QUERY = 1 << 1,  /**< driver can perform raw queries                                 */
        DB_CAP_INSERT =    1 << 2,  /**< driver can insert data                                         */
        DB_CAP_DELETE =    1 << 3,  /**< driver can delete data                                         */
        DB_CAP_UPDATE =    1 << 4,  /**< driver can update data                                         */
        DB_CAP_REPLACE =   1 << 5,  /**< driver can replace (also known as INSERT OR UPDATE) data       */
        DB_CAP_FETCH   =   1 << 6,  /**< driver supports fetch result queries                           */
        DB_CAP_LAST_INSERTED_ID = 1 << 7,  /**< driver can return the ID of the last insert operation   */
        DB_CAP_INSERT_UPDATE = 1 << 8, /**< driver can insert data into database and update on duplicate */
        DB_CAP_MULTIPLE_INSERT = 1 << 9 /**< driver can insert multiple rows at once */

} db_cap_t;

/**

 * All database capabilities except raw_query, replace, insert_update and
 * last_inserted_id which should be checked separately when needed
 */
  1. define DB_CAP_ALL (DB_CAP_QUERY | DB_CAP_INSERT | DB_CAP_DELETE | DB_CAP_UPDATE)

/* Example of usage below */ if (!DB_CAPABILITY(sql_functions, DB_CAP_ALL)) {

      LM_CRIT("Database modules does not "
            "provide all functions needed by our module\n");
      return -1;

} (:sourceend:)

April 22, 2014, at 05:50 PM by 213.233.84.113 -
Added lines 1472-1517:

OpenSIPS exposes a SQL database API that the module developers can use for operating the most common SQL queries. Advantages here are :

  • writing back-end independent code, since the DB API is decoupled from the actual modules implementing the back-end specific code
  • the ability to expose SQL-like capabilities to back-ends who are not internally SQL ( eg. the db_flatstore modules operates directly with flat-text files, yet the developer can insert into the file as if he was inserting into a regular SQL database


db/db.h exposes most of the database related functions. At startup, the developer will have just the database URL where he needs to connect. By calling, db_bind_mod , the OpenSIPS DB API will try to automatically locate the actual DB module that support that specific back-end, and will return all the needed functions for operating on the back-end.

(:source lang=C -link -getcode :) /**

 * \brief Bind database module functions                            
 *                                                                      
 * This function is special, it's only purpose is to call find_export function in
 * the core and find the addresses of all other database related functions. The
 * db_func_t callback given as parameter is updated with the found addresses.
 *                                                             
 * This function must be called before any other database API call!
 *                                                               
 * The database URL is of the form "mysql://username:password@host:port/database" or
 * "mysql" (database module name).
 * In the case of a database connection URL, this function looks only at the first
 * token (the database protocol). In the example above that would be "mysql":
 * \see db_func_t                                                            
 * \param mod database connection URL or a database module name
 * \param dbf database module callbacks to be further used                  
 * \return returns 0 if everything is OK, otherwise returns value < 0
 */                                       

int db_bind_mod(const str* mod, db_func_t* dbf);

typedef struct db_func {

      unsigned int           cap;           /* Capability vector of the database transport */
      db_use_table_f         use_table;     /* Specify table name */
      db_init_f              init;          /* Initialize database connection */
      db_close_f             close;         /* Close database connection */
      db_query_f             query;         /* query a table */
      db_fetch_result_f      fetch_result;  /* fetch result */
      db_raw_query_f         raw_query;     /* Raw query - SQL */
      db_free_result_f       free_result;   /* Free a query result */
      db_insert_f            insert;        /* Insert into table */
      db_delete_f            delete;        /* Delete from table */
      db_update_f            update;        /* Update table */
      db_replace_f           replace;       /* Replace row in a table */
      db_last_inserted_id_f  last_inserted_id;  /* Retrieve the last inserted ID
                                                    in a table */
      db_insert_update_f insert_update;     /* Insert into table, update on duplicate key */

} db_func_t;

(:sourceend:)

April 22, 2014, at 05:30 PM by 213.233.84.113 -
Changed line 1377 from:

For building the output MI tree, mi/tree.h and mi/attr.h expose the following functions :

to:

For building the output MI tree, mi/tree.h and mi/attr.h expose the following functions :

Added line 1423:
  • /
April 22, 2014, at 05:29 PM by 213.233.84.113 -
Added lines 1376-1425:


For building the output MI tree, mi/tree.h and mi/attr.h expose the following functions : (:source lang=C -link -getcode :) /* Use for creating a new output reply tree Parameters :

      code : success code for this tree ( >=200<300 for success, anything else for errors )
      reason : string reasons representation for the code
      reason_len : length of the reason parameter

Returns :

      A new mi_root tree, or NULL in case of error. Note that this function will allocate the node in PKG and it typically has to be returned - the freeing will be done in the MI core, after the output tree is written by the transport module */

struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len);

/* Adding a new child node to our tree - typically first called to mi_root->node.kids Parameters :

      parent : the parent node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
  • /

struct mi_node *add_mi_node_child(struct mi_node *parent, int flags,

        char *name, int name_len, char *value, int value_len);

/* Adding a new sibling node to one of our nodes Parameters :

      brother : the brother node for our newly added node
      flags : Current options are :
                   MI_DUP_NAME : the name of this node needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current node needs to be duplicated in PKG
      name : the name of the current node
      name_len : length of the node's name
      value : the value of the current node
      value_len : length of the node's value
  • /

struct mi_node *add_mi_node_sibling(struct mi_node *brother, int flags,

        char *name, int name_len, char *value, int value_len);

/* Adding a new attribute to one of our nodes

      node : the node we will be adding the key-value attribute to
      flags : Current options are :
                   MI_DUP_NAME : the name of this attribute needs to be duplicated in PKG 
                   MI_DUP_VALUE : the value of the current attribute needs to be duplicated in PKG
      name : the name of the current attribute
      name_len : length of the node's attribute name
      value : the value of the current value
      value_len : length of the node's attribute value

struct mi_attr *add_mi_attr(struct mi_node *node, int flags,

        char *name, int name_len, char *value, int value_len)

(:sourceend:)

April 22, 2014, at 05:15 PM by 213.233.84.113 -
Added lines 1378-1417:

(:source lang=C -link -getcode :) struct mi_root *mi_debug(struct mi_root *cmd, void *param) {

      struct mi_root *rpl_tree;
      struct mi_node *node;
      char *p;
      int len;
      int new_debug;

      /* check the kids member of our root node - 
      if the input root node has kids, our command was called with parameters */
      node = cmd->node.kids;
      if (node!=NULL) {
            /* take the node's value and convert it to int, to make sure the parameter is valid */
            if (str2sint( &node->value, &new_debug) < 0)
                  /* if failed to convert to int, still return a RPL tree with an >=400 code and reason */
                  return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
      } else
            new_debug = *debug;

      /* all is good so far, initialize a new output ROOT tree which has a 200 OK code & reason */
      rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
      if (rpl_tree==0)
              return 0;

      p = sint2str((long)new_debug, &len);
      /* add a new node to our output tree, which the current debug level */
      node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE,
             MI_SSTR("DEBUG"),p, len);
      if (node==0) {
              free_mi_tree(rpl_tree);
              return 0;
      }

      /* if all was successful, overwrite the actual debug level, and return our tree */
      *debug = new_debug;
      return rpl_tree;

} (:sourceend:)

April 22, 2014, at 05:06 PM by 213.233.84.113 -
Changed lines 1329-1332 from:
      if (register_mi_mod( "core", mi_core_cmds)<0) {
            LM_ERR("unable to register core MI cmds\n");
            return -1;  
      }  
to:

if (register_mi_mod( "core", mi_core_cmds)<0) {

      LM_ERR("unable to register core MI cmds\n");
      return -1;  

}

Added lines 1334-1377:


The structures commonly used for implementing MI functions are also found in mi/mi.h : (:source lang=C -link -getcode :) /* Parameters :

      input : the tree that contains the command paramenters
      param : the parameter provided at function registration

Returns :

      A mi_root tree containing the function reply
  • /

typedef struct mi_root* (mi_cmd_f)(struct mi_root *input, void *param);

/* below are the used structures for representing the tree root and the tree nodes */ struct mi_root {

      /* int code - similar to SIP or HTTP code */
      unsigned int       code;
      /* string reason for code - similar to SIP or HTTP reason */
      str                reason;
      /* handler in case of asynchronous MI commands */ 
      struct mi_handler  *async_hdl;
      /* the actual root node in our tree */
      struct mi_node     node; 

};

struct mi_node {

      str value;
      str name;
      unsigned int flags;
      struct mi_node *kids;
      struct mi_node *next;
      struct mi_node *last; 
      struct mi_attr *attributes;

};

struct mi_attr{

        str name;
        str value;
        struct mi_attr *next;

}; (:sourceend:)
As can be noted from the above tree definition, a node has a name and a value associated to it,it can have one or multiple children ( stored in kids pointer ), and also it can have a list of key-value attributes associated to it.
Further on, we will follow the implementation of the debug MI function. If called with no parameters, the function will return the current debug level in OpenSIPS. If called with one integer parameter, then the function will set the current debug level to the provided parameter.

April 22, 2014, at 04:56 PM by 213.233.84.113 -
Added lines 1319-1332:

/* For exporting the populated array of MI functions Parameters :

      mod_name : the name of the module exporting these functions
      mis : the array of exported MI functions

Returns :

      0 on success, negative in case of error
  • /

int register_mi_mod( char *mod_name, mi_export_t *mis);

/* Example of usage */

      if (register_mi_mod( "core", mi_core_cmds)<0) {
            LM_ERR("unable to register core MI cmds\n");
            return -1;  
      }  
April 22, 2014, at 04:51 PM by 213.233.84.113 -
Changed lines 1285-1319 from:
to:


The structures commonly used for exporting MI functions are found in mi/mi.h :

(:source lang=C -link -getcode :) typedef struct mi_export_ {

        /* the name of the function ( users will call this from their transport of choice */
        char *name;
        /* short description of the usage of this function */
        char *help;
        /* actual function that will get called */
        mi_cmd_f *cmd;
        /* flags for this function. Currently options are :
             - MI_ASYNC_RPL_FLAG - the function has an asynchronous behaviour ( eg: MI functions that send SIP messages and wait for their reply )
             - MI_NO_INPUT_FLAG - the function does not receive any parameters 
        */
        unsigned int flags;
        /* parameter that will be passed when the cmd function gets called */
        void *param;
        /* the initialization function to be called by OpenSIPS ( one time ) */
        mi_child_init_f *init_f;

}mi_export_t;

/* Example of core MI exported function */ static mi_export_t mi_core_cmds[] = {

        { "uptime", "prints various time information about OpenSIPS - "
                "when it started to run, for how long it runs",
                mi_uptime,     MI_NO_INPUT_FLAG,  0,  init_mi_uptime },
        { "version", "prints the version string of a runningOpenSIPS",
                mi_version,    MI_NO_INPUT_FLAG,  0,  0 },
        { "pwd", "prints the working directory of OpenSIPS",
                mi_pwd,        MI_NO_INPUT_FLAG,  0,  0 },

... ... ... (:sourceend:)

April 22, 2014, at 04:42 PM by 213.233.84.113 -
Added lines 1277-1285:

The Management Interface is the abstract layer that is commonly used to control and monitor OpenSIPS. The MI Interface supports multiple actual back-ends ( eg. FIFO, Datagram, XMLRPC, HTTP GET JSON, etc ) - due to the modularity of the interface and also due to the clear separation between the logic and the transport layer, the developer just defines the functions to be externally called, and then it is up to the OpenSIPS script writer to chose what transport he will actually use for controlling OpenSIPS.
The MI interface heavily uses trees :

  • the Interface will provide as input a tree with the parameters provided by the user
  • an MI function has to also return a tree, which will then be converted by the transport module to it's specific representation


Further on we will focus on the core MI functions, with a specific focus on the debug function. Note that modules can ( an commonly ) also export MI functions - see the Modules Development MI functions topic for more information on that.

April 22, 2014, at 03:24 PM by 79.112.212.8 -
Changed line 1255 from:

void update_stat(stat_var* var, int n)updated

to:

void update_stat(stat_var* var, int n);

April 19, 2014, at 09:10 PM by 79.112.212.8 -
Added line 16:
Added line 18:
Added line 24:
Added line 26:
April 19, 2014, at 08:59 PM by 79.112.212.8 -
Added line 1278:

Module Development

April 19, 2014, at 08:52 PM by 79.112.212.8 -
Added lines 1267-1270:

All statistics related code should be guarded by #ifdef STATISTICS , since the statistics are not a mandatory part of the OpenSIPS core ( they can be disabled from within menuconfig ).

April 19, 2014, at 08:50 PM by 79.112.212.8 -
Changed line 1173 from:

The most important structures used for extending statistics are the following :

to:

The most important structures used for extending statistics are exported by statistics.h :

April 19, 2014, at 07:40 PM by 79.112.212.8 -
Changed line 1218 from:

As note from the above structure, a statistics can either be a simple counter ( eg. rcv_requests ), but it can also be a function. Statistics function might come in handy when the developer needs to do extra processing on the raw counters before providing the final output.

to:

As note from the above structure, statistics can either be a simple counter ( eg. rcv_requests ), but it can also be a function. Statistics function might come in handy when the developer needs to do extra processing on the raw counters before providing the final output.

April 19, 2014, at 07:14 PM by 79.112.212.8 -
April 19, 2014, at 07:13 PM by 79.112.212.8 -
Changed line 1118 from:

Important to note here that all the above timer related functions MUST be called for the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).

to:

Important to note here that all the above timer related functions MUST be called in the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).

Changed line 1239 from:

Important to note here that all the above statistics related functions MUST be called for the context of the attendant process before forking is done.

to:

Important to note here that all the above statistics related functions MUST be called in the context of the attendant process before forking is done.

April 19, 2014, at 07:12 PM by 79.112.212.8 -
Deleted line 1234:
Deleted lines 1235-1236:
Added lines 1237-1267:

Important to note here that all the above statistics related functions MUST be called for the context of the attendant process before forking is done.


At runtime, the developer has access to the following functions for operating on statistics :

(:source lang=C -link -getcode :) /* Parameters :

      var : the statistics to be updated
      n : the value ( if positive -> stat will be increment. negative -> stat will be decremented )
  • /

void update_stat(stat_var* var, int n)updated

/* Parameters :

      var : the statistics to be reseted
  • /

void reset_stat(stat_var* var);

/* Parameters :

      var : the statistics to be fetched

Returns :

      statistic value
  • /

unsigned long get_stat_val(stat_var* var) (:sourceend:)

April 19, 2014, at 07:05 PM by 79.112.212.8 -
Added lines 1216-1221:

As note from the above structure, a statistics can either be a simple counter ( eg. rcv_requests ), but it can also be a function. Statistics function might come in handy when the developer needs to do extra processing on the raw counters before providing the final output.

\\

Changed lines 1236-1238 from:

register_module_stats will export the statistics, and also allocate them in SHM memory, for them to be accessible by all OpenSIPS processes.

to:

Note that register_module_stats will export the statistics, and also allocate them in SHM memory, for them to be accessible by all OpenSIPS processes.

April 19, 2014, at 07:02 PM by 79.112.212.8 -
Changed lines 1183-1234 from:
to:


For example, the core stats exported by OpenSIPS are defined in the following array :

(:source lang=C -link -getcode :) stat_var* rcv_reqs; stat_var* rcv_rpls; stat_var* fwd_reqs; stat_var* fwd_rpls; stat_var* drp_reqs; stat_var* drp_rpls; stat_var* err_reqs; stat_var* err_rpls; stat_var* bad_URIs; stat_var* unsupported_methods; stat_var* bad_msg_hdr;

stat_export_t core_stats[] = {

        {"rcv_requests" ,         0,  &rcv_reqs              },
        {"rcv_replies" ,          0,  &rcv_rpls              },
        {"fwd_requests" ,         0,  &fwd_reqs              },
        {"fwd_replies" ,          0,  &fwd_rpls              },
        {"drop_requests" ,        0,  &drp_reqs              },
        {"drop_replies" ,         0,  &drp_rpls              },
        {"err_requests" ,         0,  &err_reqs              },
        {"err_replies" ,          0,  &err_rpls              },
        {"bad_URIs_rcvd",         0,  &bad_URIs              },
        {"unsupported_methods",   0,  &unsupported_methods   },
        {"bad_msg_hdr",           0,  &bad_msg_hdr           },
        {"timestamp",  STAT_IS_FUNC, (stat_var**)get_ticks   },
        {0,0,0}

}; (:sourceend:)
After defining your array of statistics that you want to export, one should use the following for exporting the stats to be accessible by all

(:source lang=C -link -getcode :) /* Parameters :

      module - a string describing the module the current statistics belong to. Will be used when fetching the statistics via MI
      stats - the statistics to be registered

Returns :

      0 in case of success, negative in case of error
  • /

int register_module_stats(char *module, stat_export_t *stats; (:sourceend:)

register_module_stats will export the statistics, and also allocate them in SHM memory, for them to be accessible by all OpenSIPS processes.

\\

April 19, 2014, at 06:52 PM by 79.112.212.8 -
Changed lines 1173-1182 from:
to:

The most important structures used for extending statistics are the following :

(:source lang=C -link -getcode :) typedef struct stat_export_ {

        char* name;                /* null terminated statistic name */
        unsigned short flags;      /* flags */
        stat_var** stat_pointer;   /* pointer to the variable's mem location *
                                    * NOTE - it's in shm mem */

} stat_export_t; (:sourceend:)

April 19, 2014, at 06:49 PM by 79.112.212.8 -
Added lines 1166-1173:

OpenSIPS exposes a statistics API that can be used both from the core or the modules. The statistics are essentially counters that will be internally incremented/decremented by OpenSIPS and that can be fetched by the outside world ( via the MI interface ) for understanding the OpenSIPS load / health status / etc.
The advantages of using the OpenSIPS Statistics API instead of regular counters is :

  • easily fetched from the MI Interface
  • on supported architectures, the statistics do not use an explicit lock ( the consistency is ensured by employing assembly code ), thus you will get better performance


April 19, 2014, at 06:35 PM by 79.112.212.8 -
Changed line 647 from:
      param - the parameter provided for evaluated the pvar
to:
      param - the parameter provided for evaluating the pvar
Added line 673:
Added line 677:
Added lines 680-688:

/* Parameters :

      msg - the SIP message to apply the changes to
      param - the parameter provided for evaluating the pvar
      op - further indication on the type of write access to be done
      val - value to be pushed to our pvar

Returns :

      0 in case of success, negative in case of error
  • /
Added line 697:
      /* type checking, we can only push strings to R-URI */
Added line 703:
      /* populate the message R-URI with the string value from the provided val */
April 19, 2014, at 06:31 PM by 79.112.212.8 -
Changed lines 641-642 from:
to:


Added lines 644-651:

/* Parameters :

      msg - the message context to evaluate the current pvar 
      param - the parameter provided for evaluated the pvar
      res - the output value of our pvar 

Returns :

      0 in case of success, negative in case of error
  • /
Changed line 672 from:
to:

\\

Changed line 674 from:

For all read access on the PVARs from contexts where the PVAR does not have any meaningful value ( eg Request-URI from a Reply Context ), make sure to use pv_get_null to signal this to the script writer.

to:

For all read access on the PVARs from contexts where the PVAR does not have any meaningful value (eg. Request-URI from a Reply Context), make sure to use pv_get_null to signal this to the script writer.

Added lines 676-700:


(:source lang=C -link -getcode :) int pv_set_ruri(struct sip_msg* msg, pv_param_t *param,

                int op, pv_value_t *val)

{

      if(msg==NULL || param==NULL || val==NULL) {
            LM_ERR("bad parameters\n");
            return -1;
      }

      if(!(val->flags&PV_VAL_STR)) {
            LM_ERR("str value required to set R-URI\n");
            goto error;
      }

      if (set_ruri( msg, &val->rs)!=0) {
            LM_ERR("failed to set RURI\n");
            goto error;
      }

      return 0;

error:

      return -1;

} (:sourceend:)

April 19, 2014, at 06:18 PM by 79.112.212.8 -
Changed lines 640-666 from:

Since out $ru pvar does not support indexing by concept ( the SIP message has one and only one Request-URI ), there is no need to add a parsing or an indexing function for our pseudovariable. Also, no special initialization is needed for our psedovariable case, since we will operate directly on the currently processed SIP message in the script.

to:

Since the $ru pvar does not support indexing by concept ( the SIP message has one and only one Request-URI ), there is no need to add a parsing or an indexing function for our pseudovariable. Also, no special initialization is needed for our psedovariable case, since we will operate directly on the currently processed SIP message in the script.

(:source lang=C -link -getcode :) static int pv_get_ruri(struct sip_msg *msg, pv_param_t *param,

            pv_value_t *res)

{

      if(msg==NULL || res==NULL)
            return -1;

      if(msg->first_line.type == SIP_REPLY)   /* REPLY doesnt have a ruri */
            return pv_get_null(msg, param, res);

      if(msg->parsed_uri_ok==0 /* R-URI not parsed*/ && parse_sip_msg_uri(msg)<0) {
            LM_ERR("failed to parse the R-URI\n");
            return pv_get_null(msg, param, res);
      }

      if (msg->new_uri.s!=NULL)
            return pv_get_strval(msg, param, res, &msg->new_uri);

      return pv_get_strval(msg, param, res, &msg->first_line.u.request.uri);

} (:sourceend:)

For all read access on the PVARs from contexts where the PVAR does not have any meaningful value ( eg Request-URI from a Reply Context ), make sure to use pv_get_null to signal this to the script writer.

April 19, 2014, at 06:13 PM by 79.112.212.8 -
Added lines 626-640:

Further on we will follow the implementation of the $ru pseudovariable, which offers read/write access to the SIP message Request-URI.
First, PVT_RURI was added in pvar.h in enum _pv_type.

Afterwards, the following was added in _pv_names_table :

(:source lang=C -link -getcode :)

        {{"ru", (sizeof("ru")-1)}, /* */             
                PVT_RURI, pv_get_ruri, pv_set_ruri,  
                0, 0, 0, 0},  

(:sourceend:)

Our new pvar will be accessible from script by using $ru. Read access from the script will lead to pv_get_ruri getting called, while write requests to $ru will make a call to pv_set_ruri.
Since out $ru pvar does not support indexing by concept ( the SIP message has one and only one Request-URI ), there is no need to add a parsing or an indexing function for our pseudovariable. Also, no special initialization is needed for our psedovariable case, since we will operate directly on the currently processed SIP message in the script.

April 19, 2014, at 06:00 PM by 79.112.212.8 -
Added lines 599-625:


The general syntax of an OpenSIPS pseudo-variable, along with the pv_export_t structure to be used in OpenSIPS ( both in core and in modules ) to expose new PVARs are shown below :

(:source lang=C -link -getcode :) /*! \brief

 * PV spec format:
 * - $class_name
 * - $class_name(inner_name)
 * - $(class_name[index])
 * - $(class_name(inner_name)[index])
 * - $(class_name{transformation})
 * - $(class_name(inner_name){transformation})
 * - $(class_name[index]{transformation})            
 * - $(class_name(inner_name)[index]{transformation})
 */             

typedef struct _pv_export {

        str name;                      /*!< class name of PV */
        pv_type_t type;                /*!< type of PV */
        pv_getf_t  getf;               /*!< function to get the value */
        pv_setf_t  setf;               /*!< function to set the value */
        pv_parse_name_f parse_name;    /*!< function to parse the inner name */
        pv_parse_index_f parse_index;  /*!< function to parse the index of PV */
        pv_init_param_f init_param;    /*!< function to init the PV spec */
        int iparam;                    /*!< parameter for the init function */

} pv_export_t; (:sourceend:) \\

April 19, 2014, at 05:57 PM by 79.112.212.8 -
Changed lines 15-18 from:

TBD TBD TBD

to:

TBD
TBD
TBD

Changed lines 21-23 from:

TBD TBD TBD

to:

TBD \\ TBD
TBD \\

April 19, 2014, at 05:56 PM by 79.112.212.8 -
Changed lines 16-18 from:
to:

TBD TBD

Added lines 21-22:

TBD TBD

April 19, 2014, at 05:56 PM by 79.112.212.8 -
Added lines 15-16:

TBD

Added lines 18-19:

TBD

April 19, 2014, at 05:55 PM by 79.112.212.8 -
Changed lines 569-571 from:
Adding Core Pseudo-Variable

TBD

to:
Adding a core Pseudo-Variable

All the OpenSIPS core pseudo-variables are defined in pvar.c : (:source lang=C -link -getcode :) static pv_export_t _pv_names_table[] = {

      {{"avp", (sizeof("avp")-1)}, PVT_AVP, pv_get_avp, pv_set_avp,
            pv_parse_avp_name, pv_parse_index, 0, 0},
      {{"hdr", (sizeof("hdr")-1)}, PVT_HDR, pv_get_hdr, 0, pv_parse_hdr_name,
            pv_parse_index, 0, 0},               
      {{"hdrcnt", (sizeof("hdrcnt")-1)}, PVT_HDRCNT, pv_get_hdrcnt, 0, pv_parse_hdr_name, 0, 0, 0},
      {{"var", (sizeof("var")-1)}, PVT_SCRIPTVAR, pv_get_scriptvar,
            pv_set_scriptvar, pv_parse_scriptvar_name, 0, 0, 0},
      {{"ai", (sizeof("ai")-1)}, /* */             
            PVT_PAI_URI, pv_get_pai, 0,          
            0, 0, 0, 0},  
      {{"au", (sizeof("au")-1)}, /* */
            PVT_AUTH_USERNAME, pv_get_authattr, 0,
            0, 0, pv_init_iname, 1},

... ... ... (:sourceend:)

April 19, 2014, at 05:49 PM by 79.112.212.8 -
Added lines 569-572:
Adding Core Pseudo-Variable

TBD

Added lines 1025-1029:

Management Interface API

SQL Database API

NoSQL API

April 19, 2014, at 05:46 PM by 79.112.212.8 -
Added lines 1018-1021:

Statistics API

April 19, 2014, at 05:46 PM by 79.112.212.8 -
Changed lines 456-458 from:

Locking API

OpenSIPS has it's own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the usage case and the menuconfig provided compilation flags, the OpenSIPS generic locks can be converted either to busy locks, futexes, SysV locks, etc.

to:

Extending OpenSIPS core Config File

OpenSIPS uses flex and bison in order to parse the configuration file and then build the entire action tree that a SIP message will go through once it is read from network level.
When it comes to extending the OpenSIPS configuration file directly in the core, the developer can either choose to add a new core parameter, or a new core function.

Adding a core parameter

In the following step by step tutorial, we will follow the implementation of the children core parameter, which is an integer controlling the number of OpenSIPS processes per UDP interface.
First of all, we will have to add the variable that will hold the value of our new core parameter.
In our case, in globals.h we have added (:source lang=C -link -getcode :) extern int children_no; (:sourceend:)

Changed lines 470-475 from:

The Locking API offers two distinct functionalities, one for using single lock elements, and another for operating on entire sets of locks.

Single Lock API

The API can be used by including “locking.h” . The OpenSIPS generic lock is defined by the gen_lock_t structure.
Allocating a new lock is done by calling lock_alloc :

to:

Adding the variable here will make it visible in both the OpenSIPS core and the OpenSIPS modules. main.c will hold the actual variable :

Changed lines 472-476 from:

/* Returns :

      A shared memory allocated lock, or NULL in case of an error.
  • /

gen_lock_t *lock_alloc(void);

to:

/* Default value in case the parameter is not set from the script */ int children_no = 8;

Changed lines 476-477 from:

Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock separately - if the lock is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock, it must be initialized :

to:

Next, we will have to modify the grammar in order to accept our new parameter. In cfg.y , first we will define our new token :

Changed lines 479-485 from:

/* Parameters :

      lock - the lock instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_t* lock_init(gen_lock_t* lock);

to:

%token CHILDREN

Changed line 482 from:

In order to acquire a lock, one must use the lock_get function :

to:

And then we'll set the parsing rules for the new token

Changed lines 484-488 from:

/* Parameters :

      lock - the lock to be acquired
  • /

void lock_get(gen_lock_t *lock);

to:

| CHILDREN EQUAL NUMBER { children_no=$3; } | CHILDREN EQUAL error { yyerror("number expected"); }

Changed lines 488-490 from:

The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_release should be used :

to:

Our variable will have a numeric variable, anything else will trigger and error in parsing the OpenSIPS script.

Adding a core function

In the following step by step tutorial, we will follow the implementation of the xlog core parameter, which is used to print debugging information to the logging facility.

Note that xlog can receive either a single parameter ( the string to be printed ), or two parameters ( the log level and then the string to be printed ).

First, the grammar will have to be extended. In cfg,y we have :

Changed lines 500-504 from:

/* Parameters :

      lock - the lock to be released.
  • /

void lock_release(gen_lock_t *lock);

to:

%token XLOG ... ... | XLOG LPAREN STRING RPAREN {

      mk_action1($$, XLOG_T, STR_ST, $3); }                             
 XLOG LPAREN STRING COMMA STRING RPAREN {                                        
      mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); } 
Changed lines 509-511 from:

Once a lock is no longer needed, one must first destroy the lock, and then the lock can be safely deallocated.

to:

Note the two different forms of xlog displayed above.
From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done. The fixup part will be invoked only once, at script parsing.

Changed lines 513-522 from:

/* Parameters :

      lock - the lock to be destroyed
  • /

void lock_destroy(gen_lock_t *lock); /* Parameters :

      lock - the lock to be deallocated
  • /

void lock_dealloc(gen_lock_t *lock);

to:

case XLOG_T:

      s.s = (char*)t->elem[1].u.data; 
      if (s.s == NULL) { 
            /* commands have only one parameter */ 
            s.s = (char *)t->elem[0].u.data; 
            s.len = strlen(s.s); 
            if(s.len==0) { 
                  LM_ERR("param is empty string!\n"); 
                  return E_CFG; 
            } 

            /* parse the format provided to xlog - we can have variables inside */
            if(pv_parse_format(&s ,&model) || model==NULL) { 
                  LM_ERR("wrong format [%s] for value param!\n", s.s); 
                  ret=E_BUG; 
                  goto error; 
            } 

            /* overwrite the data that will be passed to contain our new parsed model */
            t->elem[0].u.data = (void*)model; 
            t->elem[0].type = SCRIPTVAR_ELEM_ST; 
      } else {
            /* two parameters */
Changed line 538 from:

Here is a code snippet showing the typical code used when dealing with single lock instances :

to:

In action.c we will have to add the actual code that needs to get executed when the function gets called from the OpenSIPS script at runtime :

Changed lines 540-586 from:

gen_lock_t *my_lock;

int init_function(void) {

	/* … */

	my_lock = lock_alloc();
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock \n”);
		return -1;
	}

	if (lock_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */
	lock_get(my_lock)


	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_release(my_lock)
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_destroy(my_lock);
	lock_dealloc(my_lock);

	/* … */

}

to:

case XLOG_T:

      /* add helpers for tracing the script */
      script_trace("core", "xlog", msg, a->line) ;
      if (a->elem[1].u.data != NULL) {
            /* we have two parameters */ 

            /* do security checks for the types of the provided parameters, second param has to be a SCRIPTVAR model as we've coded in the fixup */
            if (a->elem[1].type != SCRIPTVAR_ELEM_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type); 
                  ret=E_BUG; 
                  break; 
            }
            /* log level should be a plaintext string */ 
            if (a->elem[0].type != STR_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); 
                  ret=E_BUG; 
                  break; 
            }

            /* call our C code function implementing the desired actions */
            if (xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data) < 0) { 
                  LM_ALERT("Cannot print xlog debug message"); 
                  break;
            }
      } else {
            /* one parameter case */
      }
Changed lines 569-572 from:
Lock Set API

Operating on an entire array of locks can become very useful when dealing with structures like hashes, where you would need a lock per each hash entry.
The API can be used by including locking.h . The OpenSIPS generic array of locks is defined by the gen_lock_set_t structure, and working with it is very similar in concept to operating a single lock entry.

to:

Adding Transformations

The so called transformations are methods operating directly on the OpenSIPS various pseudo-variables. A transformation takes as input the value of the provided pseudo-variable and processes it, outputing a 'transformed' version.

Changed lines 573-574 from:

Allocating a new lock set is done by calling lock_set_alloc :

to:

Transformation examples are the URI transformations ( working on a SIP URI, and they allow extracting various useful information from the URI, like the user , domain, various parameters, etc ), the S class of transformations ( work on generic strings, and provide various useful methods as fetching the length of the string, searching the first occurrence of a character inside the string, etc ), and many others – see http://www.opensips.org/Documentation/Script-Tran-1-10 \\

Changed lines 576-580 from:

/* Returns :

      A shared memory allocated lock set, or NULL in case of an error.
  • /

gen_lock_set_t *lock_set_alloc(void);

to:
  1. example of usage

$var(tutorial) = “OpenSIPSDevel”; xlog(“Our variable has $(var(tutorial){s.len}) characters \n”);

Changed lines 581-582 from:

Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock set separately - if the lock set is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock set, it must be initialized :

to:

Note that transformations can be chained together, which will have an impact on the transformations C development

\\

Changed lines 588-594 from:

/* Parameters :

      lock - the lock set instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_set_t* lock_set_init(gen_lock_set_t* lock);

to:

$var(our_uri) = “sip:vlad@opensips.org”; xlog(“The username of our URI has $(var(our_uri){uri.user}{s.len}) characters \n”);

Changed lines 592-597 from:

In order to acquire a lock in a lock set, one must use the lock_set_get function :

to:

In our examples, uri and s are the so called classes of transformations, while user and len are the actual operations within the class.
We will further follow the implementation of the uri class of transformation, and then focusing on the user function.
Adding new classes of transformations is done in transformations.h , where , for our example's case, TR_URI was added. Then, each class of transformations should have it's own functions enumeration, which is our case is enum _tr_uri_subtype .
transformations.c holds the actual implementation of transformations. parse_transformation will get called when the script needs to evaluate a transformation. There, we should add the matching for our new class of transformations :

Changed lines 599-604 from:

/* Parameters :

      lock - the lock to be acquired
      entry - the entry in the lock set that needs to be acquired
  • /

void lock_set_get(gen_lock_set_t *lock,int entry);

to:

else if(tclass.len==3 && strncasecmp(tclass.s, "uri", 3)==0) {

      t->type = TR_URI; 
      t->trf = tr_eval_uri; 
      s.s = p; s.len = in->s + in->len - p; 
      p0 = tr_parse_uri(&s, t); 
      if(p0==NULL) 
            goto error; 
      p = p0; 

}

Changed lines 610-612 from:

The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_set_release should be used :

to:

Here, there are two main functions of interest. First, there is the tr_parse_uri function responsible for matching the actual function that needs to get executed inside our class of transformation, doing whatever parsing that is necessary for the transformation and advancing the position of the current pointer in the transformation string. This is a fixup type function and is only evaluated once :

Changed lines 613-618 from:

/* Parameters :

      lock - the lock to be released.
      entry - the entry in the lock set that needs to be released
  • /

void lock_set_release(gen_lock_set_t *lock,int entry);

to:

p = in->s; name.s = in->s;

/* find next token */ while(*p && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') {

      LM_ERR("invalid transformation: %.*s\n", in->len, in->s);
      goto error;

} name.len = p - name.s; trim(&name);

if(name.len==4 && strncasecmp(name.s, "user", 4)==0) {

      t->subtype = TR_URI_USER;
      return p;

}

Changed lines 631-632 from:

Once a lock set is no longer needed, one must first destroy the lock set, and then the lock set can be safely deallocated.

to:

After we've decided what actually needs to be done and did all the parsing,the tr_eval_uri will be the one gets called at runtime and that actually evaluates our transformation and returns the new transformation output.
In our case, the function parses the provided string into a SIP URI, and then populates the output pv_value_t accordingly to the subtype provided :

Changed lines 634-643 from:

/* Parameters :

      lock - the lock set to be destroyed
  • /

void lock_set_destroy(gen_lock_set_t *lock); /* Parameters :

      lock - the lock set to be deallocated
  • /

void lock_set_dealloc(gen_lock_set_t *lock);

to:

/* make a PKG copy of the input */ _tr_uri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); memcpy(_tr_uri.s, val->rs.s, val->rs.len); _tr_uri.s[_tr_uri.len] = '\0'; ... ... /* parse the input URI */ if(parse_uri(_tr_uri.s, _tr_uri.len, &_tr_parsed_uri)!=0) {

      LM_ERR("invalid uri [%.*s]\n", val->rs.len,val->rs.s);
      return -1;

} ... ... /* zero out the output val */ memset(val, 0, sizeof(pv_value_t)); /* the output pvar will be a string */ val->flags = PV_VAL_STR;

switch(subtype) {

      case TR_URI_USER:
            val->rs = (_tr_parsed_uri.user.s)?_tr_parsed_uri.user:_tr_empty;
            break;

}

Added lines 658-660:

Locking API

OpenSIPS has it's own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the usage case and the menuconfig provided compilation flags, the OpenSIPS generic locks can be converted either to busy locks, futexes, SysV locks, etc.

Changed lines 662-667 from:

Here is a code snippet showing the typical code used when dealing with single lock instances :

to:

The Locking API offers two distinct functionalities, one for using single lock elements, and another for operating on entire sets of locks.

Single Lock API

The API can be used by including “locking.h” . The OpenSIPS generic lock is defined by the gen_lock_t structure.
Allocating a new lock is done by calling lock_alloc :

Changed lines 669-719 from:

gen_lock_set_t *my_lock;

int init_function(void) {

	/* … */

        /* allocate lock set with 32 entries */
	my_lock = lock_set_alloc(32);
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock set \n”);
		return -1;
	}

	if (lock_set_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock set \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */

	/* acquire entry 5 in the lock set */
	lock_set_get(my_lock,5)

	/* also acquire entry 21 in the lock set */
	lock_set_get(my_lock,21);

	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_set_release(my_lock,21);
	lock_set_release(my_lock,5);
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_set_destroy(my_lock);
	lock_set_dealloc(my_lock);

	/* … */

}

to:

/* Returns :

      A shared memory allocated lock, or NULL in case of an error.
  • /

gen_lock_t *lock_alloc(void);

Changed lines 675-684 from:

Timer API

OpenSIPS exposes it's own API for implementing timer functions, with seconds and microsecond precision.
The OpenSIPS timer architecture is made out of the so called 'timer keeper' , which is a process which just increments global counter of second and microsecond precision, and one process which will execute the various timer functions when their time to execute has arrived.
Also, the OpenSIPS timers support automatic re-calibration in order to compensate timer drifting in case the timer executed functions do not complete in timely manners.

Global Timer Process

timer.h exposes all the relevant functionalities for operating the OpenSIPS timers. For registering a new timer function with second precision, use :

to:


Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock separately - if the lock is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock, it must be initialized :

Changed lines 681-684 from:
	label – opaque string containing a short timer function description ( to be used for logging )
	f – the actual function to be called
	param – parameter to be provided to the timer function
	interval – the interval, in seconds, that the function needs to be called at
to:
      lock - the lock instance to be initialized
Changed line 683 from:
	0 in case of success, negative code in case of internal error.
to:
      The initialized lock in case of success, or NULL in case of error.
Changed lines 685-686 from:

int register_timer(char *label, timer_function f, void* param,

      unsigned int interval);
to:

gen_lock_t* lock_init(gen_lock_t* lock); (:sourceend:)
In order to acquire a lock, one must use the lock_get function : (:source lang=C -link -getcode :)

Deleted line 690:

The seconds callback

Changed lines 692-693 from:
      ticks - represents the current number of seconds since OpenSIPS startup when the callback is called at
      param - is the parameter provided at timer function registration.
to:
      lock - the lock to be acquired
Changed line 694 from:

typedef void (timer_function)(unsigned int ticks, void* param);

to:

void lock_get(gen_lock_t *lock);

Changed lines 697-699 from:

For registering a microsecond timer, you should use

to:

The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_release should be used :

Changed lines 703-708 from:
	label – opaque string containing a short timer function description ( to be used for logging )
	f – the actual function to be called
	param – parameter to be provided to the timer function
	interval – the interval, in microseconds, that the function needs to be called at

Returns :

	0 in case of success, negative code in case of internal error.
to:
      lock - the lock to be released.
Changed line 705 from:

int register_utimer(char *label, utimer_function f, void* param, unsigned int interval);

to:

void lock_release(gen_lock_t *lock);

Changed lines 707-712 from:
Dedicated Timer Process

Since, by default, all the registered timer functions are called from within the same process context, in case you are writing a timer process that is doing I/O, it is better to register an entirely new process where to run your code, since your function might slow down all the other timer functions running in OpenSIPS.
Registering a new timer process can be done by calling :

to:


Once a lock is no longer needed, one must first destroy the lock, and then the lock can be safely deallocated.

Changed lines 712-718 from:
      label - opaque string containing a short timer function description ( to be used for logging )
      f - the actual function to be called
      param - parameter to be provided to the timer function
      interval - the interval, in seconds, that the function needs to be called at
      flags - flags controlling process behavior. Currently only option is TIMER_PROC_INIT_FLAG , which leads to child_init to be called in the new timer process context. To be used when inside the timer you need to operate various I/O options which generally require a per process connection.

Returns :

      struct sr_timer_process pointer in case of success, or NULL in case of error.
to:
      lock - the lock to be destroyed
Changed lines 714-719 from:

void* register_timer_process(char *label, timer_function f, void* param,

      unsigned int interval, unsigned int flags);

(:sourceend:)
The output struct sr_timer_process pointer can be further use to group together multiple functions inside the same process, by calling : (:source lang=C -link -getcode :)

to:

void lock_destroy(gen_lock_t *lock);

Changed lines 716-723 from:

Parameters:

      label – opaque string containing a short timer function description ( to be used for logging )
      f – the actual function to be called
      param – parameter to be provided to the timer function
      interval – the interval, in seconds, that the function needs to be called at
      timer – the  struct sr_timer_process pointer obtained by previously calling  register_timer_process

Returns:

      0 in case of success, negative code in case of internal error.
to:

Parameters :

      lock - the lock to be deallocated
Changed lines 719-720 from:

int append_timer_to_process( char *label, timer_function f, void* param,

      unsigned int interval, void *timer);
to:

void lock_dealloc(gen_lock_t *lock);

Changed lines 722-727 from:

Important to note here that all the above timer related functions MUST be called for the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).

Below is a code snippet exemplifying how the dialog module's code used for registering two timers, with an option to either use the global timer process or to have it's own separate timer :

to:

Here is a code snippet showing the typical code used when dealing with single lock instances :

Changed lines 724-748 from:

if (dlg_have_own_timer_proc) {

      LM_INFO("Running with dedicated dialog timer process\n"); 
      dlg_own_timer_proc = register_timer_process( "dlg-timer", 
            dlg_timer_routine, NULL,1,TIMER_PROC_INIT_FLAG ); 
      if (dlg_own_timer_proc == NULL) { 
            LM_ERR("Failed to init dialog own timer proc\n"); 
            return -1; 
      } 
      if (append_timer_to_process("dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval,dlg_own_timer_proc) < 0) { 
            LM_ERR("Failed to append ping timer \n"); 
            return -1; 
      }

} else {

      if ( register_timer( "dlg-timer", dlg_timer_routine, NULL, 1)<0 ) { 
            LM_ERR("failed to register timer \n"); 
            return -1; 
      } 

      if ( register_timer( "dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval)<0) { 
            LM_ERR("failed to register timer 2 \n"); 
            return -1; 
      } 

}

to:

gen_lock_t *my_lock;

int init_function(void) {

	/* … */

	my_lock = lock_alloc();
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock \n”);
		return -1;
	}

	if (lock_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */
	lock_get(my_lock)


	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_release(my_lock)
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_destroy(my_lock);
	lock_dealloc(my_lock);

	/* … */

}

Added lines 772-776:
Lock Set API

Operating on an entire array of locks can become very useful when dealing with structures like hashes, where you would need a lock per each hash entry.
The API can be used by including locking.h . The OpenSIPS generic array of locks is defined by the gen_lock_set_t structure, and working with it is very similar in concept to operating a single lock entry.

Changed line 778 from:

Also, the OpenSIPS API also exposes the number of seconds and microseconds passed from the OpenSIPS start time. These can be accessed by calling

to:

Allocating a new lock set is done by calling lock_set_alloc :

Changed lines 780-782 from:

/* Returns :

      the number of seconds elapsed from OpenSIPS start
to:

/* Returns :

      A shared memory allocated lock set, or NULL in case of an error.
Changed lines 784-790 from:

unsigned int get_ticks(void);

/* Returns:

      the number of microseconds elapsed from OpenSIPS start
  • /

utime_t get_uticks(void);

to:

gen_lock_set_t *lock_set_alloc(void);

Deleted lines 785-798:

Extending OpenSIPS core Config File

OpenSIPS uses flex and bison in order to parse the configuration file and then build the entire action tree that a SIP message will go through once it is read from network level.
When it comes to extending the OpenSIPS configuration file directly in the core, the developer can either choose to add a new core parameter, or a new core function.

Adding a core parameter

In the following step by step tutorial, we will follow the implementation of the children core parameter, which is an integer controlling the number of OpenSIPS processes per UDP interface.
First of all, we will have to add the variable that will hold the value of our new core parameter.
In our case, in globals.h we have added (:source lang=C -link -getcode :) extern int children_no; (:sourceend:)

Changed lines 787-788 from:

Adding the variable here will make it visible in both the OpenSIPS core and the OpenSIPS modules. main.c will hold the actual variable :

to:

Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock set separately - if the lock set is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock set, it must be initialized :

Changed lines 790-791 from:

/* Default value in case the parameter is not set from the script */ int children_no = 8;

to:

/* Parameters :

      lock - the lock set instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_set_t* lock_set_init(gen_lock_set_t* lock);

Changed lines 799-800 from:

Next, we will have to modify the grammar in order to accept our new parameter. In cfg.y , first we will define our new token :

to:

In order to acquire a lock in a lock set, one must use the lock_set_get function :

Changed lines 801-806 from:

%token CHILDREN

to:

/* Parameters :

      lock - the lock to be acquired
      entry - the entry in the lock set that needs to be acquired
  • /

void lock_set_get(gen_lock_set_t *lock,int entry);

Changed lines 809-811 from:

And then we'll set the parsing rules for the new token

to:

The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_set_release should be used :

Changed lines 813-814 from:

| CHILDREN EQUAL NUMBER { children_no=$3; } | CHILDREN EQUAL error { yyerror("number expected"); }

to:

/* Parameters :

      lock - the lock to be released.
      entry - the entry in the lock set that needs to be released
  • /

void lock_set_release(gen_lock_set_t *lock,int entry);

Changed lines 821-831 from:

Our variable will have a numeric variable, anything else will trigger and error in parsing the OpenSIPS script.

Adding a core function

In the following step by step tutorial, we will follow the implementation of the xlog core parameter, which is used to print debugging information to the logging facility.

Note that xlog can receive either a single parameter ( the string to be printed ), or two parameters ( the log level and then the string to be printed ).

First, the grammar will have to be extended. In cfg,y we have :

to:

Once a lock set is no longer needed, one must first destroy the lock set, and then the lock set can be safely deallocated.

Changed lines 823-829 from:

%token XLOG ... ... | XLOG LPAREN STRING RPAREN {

      mk_action1($$, XLOG_T, STR_ST, $3); }                             
 XLOG LPAREN STRING COMMA STRING RPAREN {                                        
      mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); } 
to:

/* Parameters :

      lock - the lock set to be destroyed
  • /

void lock_set_destroy(gen_lock_set_t *lock); /* Parameters :

      lock - the lock set to be deallocated
  • /

void lock_set_dealloc(gen_lock_set_t *lock);

Changed lines 835-837 from:

Note the two different forms of xlog displayed above.
From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done. The fixup part will be invoked only once, at script parsing.

to:

Here is a code snippet showing the typical code used when dealing with single lock instances :

Changed lines 837-859 from:

case XLOG_T:

      s.s = (char*)t->elem[1].u.data; 
      if (s.s == NULL) { 
            /* commands have only one parameter */ 
            s.s = (char *)t->elem[0].u.data; 
            s.len = strlen(s.s); 
            if(s.len==0) { 
                  LM_ERR("param is empty string!\n"); 
                  return E_CFG; 
            } 

            /* parse the format provided to xlog - we can have variables inside */
            if(pv_parse_format(&s ,&model) || model==NULL) { 
                  LM_ERR("wrong format [%s] for value param!\n", s.s); 
                  ret=E_BUG; 
                  goto error; 
            } 

            /* overwrite the data that will be passed to contain our new parsed model */
            t->elem[0].u.data = (void*)model; 
            t->elem[0].type = SCRIPTVAR_ELEM_ST; 
      } else {
            /* two parameters */
to:

gen_lock_set_t *my_lock;

int init_function(void) {

	/* … */

        /* allocate lock set with 32 entries */
	my_lock = lock_set_alloc(32);
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock set \n”);
		return -1;
	}

	if (lock_set_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock set \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */

	/* acquire entry 5 in the lock set */
	lock_set_get(my_lock,5)

	/* also acquire entry 21 in the lock set */
	lock_set_get(my_lock,21);

	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_set_release(my_lock,21);
	lock_set_release(my_lock,5);
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_set_destroy(my_lock);
	lock_set_dealloc(my_lock);

	/* … */

}

Changed lines 889-890 from:


In action.c we will have to add the actual code that needs to get executed when the function gets called from the OpenSIPS script at runtime :

to:

Timer API

OpenSIPS exposes it's own API for implementing timer functions, with seconds and microsecond precision.
The OpenSIPS timer architecture is made out of the so called 'timer keeper' , which is a process which just increments global counter of second and microsecond precision, and one process which will execute the various timer functions when their time to execute has arrived.
Also, the OpenSIPS timers support automatic re-calibration in order to compensate timer drifting in case the timer executed functions do not complete in timely manners.

Global Timer Process

timer.h exposes all the relevant functionalities for operating the OpenSIPS timers. For registering a new timer function with second precision, use :

Changed lines 900-926 from:

case XLOG_T:

      /* add helpers for tracing the script */
      script_trace("core", "xlog", msg, a->line) ;
      if (a->elem[1].u.data != NULL) {
            /* we have two parameters */ 

            /* do security checks for the types of the provided parameters, second param has to be a SCRIPTVAR model as we've coded in the fixup */
            if (a->elem[1].type != SCRIPTVAR_ELEM_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type); 
                  ret=E_BUG; 
                  break; 
            }
            /* log level should be a plaintext string */ 
            if (a->elem[0].type != STR_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); 
                  ret=E_BUG; 
                  break; 
            }

            /* call our C code function implementing the desired actions */
            if (xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data) < 0) { 
                  LM_ALERT("Cannot print xlog debug message"); 
                  break;
            }
      } else {
            /* one parameter case */
      }
to:

/* Parameters :

	label – opaque string containing a short timer function description ( to be used for logging )
	f – the actual function to be called
	param – parameter to be provided to the timer function
	interval – the interval, in seconds, that the function needs to be called at

Returns :

	0 in case of success, negative code in case of internal error.
  • /

int register_timer(char *label, timer_function f, void* param,

      unsigned int interval);

/* The seconds callback Parameters :

      ticks - represents the current number of seconds since OpenSIPS startup when the callback is called at
      param - is the parameter provided at timer function registration.
  • /

typedef void (timer_function)(unsigned int ticks, void* param);

Deleted lines 918-921:

Adding Transformations

The so called transformations are methods operating directly on the OpenSIPS various pseudo-variables. A transformation takes as input the value of the provided pseudo-variable and processes it, outputing a 'transformed' version.

Changed lines 920-921 from:

Transformation examples are the URI transformations ( working on a SIP URI, and they allow extracting various useful information from the URI, like the user , domain, various parameters, etc ), the S class of transformations ( work on generic strings, and provide various useful methods as fetching the length of the string, searching the first occurrence of a character inside the string, etc ), and many others – see http://www.opensips.org/Documentation/Script-Tran-1-10 \\

to:

For registering a microsecond timer, you should use

Changed lines 922-924 from:
  1. example of usage

$var(tutorial) = “OpenSIPSDevel”; xlog(“Our variable has $(var(tutorial){s.len}) characters \n”);

to:

/* Parameters :

	label – opaque string containing a short timer function description ( to be used for logging )
	f – the actual function to be called
	param – parameter to be provided to the timer function
	interval – the interval, in microseconds, that the function needs to be called at

Returns :

	0 in case of success, negative code in case of internal error.
  • /

int register_utimer(char *label, utimer_function f, void* param, unsigned int interval);

Changed lines 933-939 from:


Note that transformations can be chained together, which will have an impact on the transformations C development

\\

to:
Dedicated Timer Process

Since, by default, all the registered timer functions are called from within the same process context, in case you are writing a timer process that is doing I/O, it is better to register an entirely new process where to run your code, since your function might slow down all the other timer functions running in OpenSIPS.
Registering a new timer process can be done by calling :

Changed lines 940-941 from:

$var(our_uri) = “sip:vlad@opensips.org”; xlog(“The username of our URI has $(var(our_uri){uri.user}{s.len}) characters \n”);

to:

/* Parameters :

      label - opaque string containing a short timer function description ( to be used for logging )
      f - the actual function to be called
      param - parameter to be provided to the timer function
      interval - the interval, in seconds, that the function needs to be called at
      flags - flags controlling process behavior. Currently only option is TIMER_PROC_INIT_FLAG , which leads to child_init to be called in the new timer process context. To be used when inside the timer you need to operate various I/O options which generally require a per process connection.

Returns :

      struct sr_timer_process pointer in case of success, or NULL in case of error.
  • /

void* register_timer_process(char *label, timer_function f, void* param,

      unsigned int interval, unsigned int flags);
Changed lines 954-959 from:

In our examples, uri and s are the so called classes of transformations, while user and len are the actual operations within the class.
We will further follow the implementation of the uri class of transformation, and then focusing on the user function.
Adding new classes of transformations is done in transformations.h , where , for our example's case, TR_URI was added. Then, each class of transformations should have it's own functions enumeration, which is our case is enum _tr_uri_subtype .
transformations.c holds the actual implementation of transformations. parse_transformation will get called when the script needs to evaluate a transformation. There, we should add the matching for our new class of transformations :

to:

The output struct sr_timer_process pointer can be further use to group together multiple functions inside the same process, by calling :

Changed lines 956-964 from:

else if(tclass.len==3 && strncasecmp(tclass.s, "uri", 3)==0) {

      t->type = TR_URI; 
      t->trf = tr_eval_uri; 
      s.s = p; s.len = in->s + in->len - p; 
      p0 = tr_parse_uri(&s, t); 
      if(p0==NULL) 
            goto error; 
      p = p0; 

}

to:

/* Parameters:

      label – opaque string containing a short timer function description ( to be used for logging )
      f – the actual function to be called
      param – parameter to be provided to the timer function
      interval – the interval, in seconds, that the function needs to be called at
      timer – the  struct sr_timer_process pointer obtained by previously calling  register_timer_process

Returns:

      0 in case of success, negative code in case of internal error.
  • /

int append_timer_to_process( char *label, timer_function f, void* param,

      unsigned int interval, void *timer);
Changed lines 970-971 from:

Here, there are two main functions of interest. First, there is the tr_parse_uri function responsible for matching the actual function that needs to get executed inside our class of transformation, doing whatever parsing that is necessary for the transformation and advancing the position of the current pointer in the transformation string. This is a fixup type function and is only evaluated once :

to:

Important to note here that all the above timer related functions MUST be called for the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).

Below is a code snippet exemplifying how the dialog module's code used for registering two timers, with an option to either use the global timer process or to have it's own separate timer :

Changed lines 977-992 from:

p = in->s; name.s = in->s;

/* find next token */ while(*p && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') {

      LM_ERR("invalid transformation: %.*s\n", in->len, in->s);
      goto error;

} name.len = p - name.s; trim(&name);

if(name.len==4 && strncasecmp(name.s, "user", 4)==0) {

      t->subtype = TR_URI_USER;
      return p;

}

to:

if (dlg_have_own_timer_proc) {

      LM_INFO("Running with dedicated dialog timer process\n"); 
      dlg_own_timer_proc = register_timer_process( "dlg-timer", 
            dlg_timer_routine, NULL,1,TIMER_PROC_INIT_FLAG ); 
      if (dlg_own_timer_proc == NULL) { 
            LM_ERR("Failed to init dialog own timer proc\n"); 
            return -1; 
      } 
      if (append_timer_to_process("dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval,dlg_own_timer_proc) < 0) { 
            LM_ERR("Failed to append ping timer \n"); 
            return -1; 
      }

} else {

      if ( register_timer( "dlg-timer", dlg_timer_routine, NULL, 1)<0 ) { 
            LM_ERR("failed to register timer \n"); 
            return -1; 
      } 

      if ( register_timer( "dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval)<0) { 
            LM_ERR("failed to register timer 2 \n"); 
            return -1; 
      } 

}

Changed lines 1004-1005 from:

After we've decided what actually needs to be done and did all the parsing,the tr_eval_uri will be the one gets called at runtime and that actually evaluates our transformation and returns the new transformation output.
In our case, the function parses the provided string into a SIP URI, and then populates the output pv_value_t accordingly to the subtype provided :

to:

Also, the OpenSIPS API also exposes the number of seconds and microseconds passed from the OpenSIPS start time. These can be accessed by calling

Changed lines 1006-1029 from:

/* make a PKG copy of the input */ _tr_uri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); memcpy(_tr_uri.s, val->rs.s, val->rs.len); _tr_uri.s[_tr_uri.len] = '\0'; ... ... /* parse the input URI */ if(parse_uri(_tr_uri.s, _tr_uri.len, &_tr_parsed_uri)!=0) {

      LM_ERR("invalid uri [%.*s]\n", val->rs.len,val->rs.s);
      return -1;

} ... ... /* zero out the output val */ memset(val, 0, sizeof(pv_value_t)); /* the output pvar will be a string */ val->flags = PV_VAL_STR;

switch(subtype) {

      case TR_URI_USER:
            val->rs = (_tr_parsed_uri.user.s)?_tr_parsed_uri.user:_tr_empty;
            break;

} (:sourceend:)

to:

/* Returns :

      the number of seconds elapsed from OpenSIPS start
  • /

unsigned int get_ticks(void);

/* Returns:

      the number of microseconds elapsed from OpenSIPS start
  • /

utime_t get_uticks(void); (:sourceend:)

April 19, 2014, at 04:20 PM by 79.112.212.8 -
Added line 230:
  • /
April 19, 2014, at 02:52 PM by 79.112.212.8 -
Added lines 967-1015:

} (:sourceend:)
Here, there are two main functions of interest. First, there is the tr_parse_uri function responsible for matching the actual function that needs to get executed inside our class of transformation, doing whatever parsing that is necessary for the transformation and advancing the position of the current pointer in the transformation string. This is a fixup type function and is only evaluated once :

(:source lang=C -link -getcode :) p = in->s; name.s = in->s;

/* find next token */ while(*p && *p!=TR_PARAM_MARKER && *p!=TR_RBRACKET) p++; if(*p=='\0') {

      LM_ERR("invalid transformation: %.*s\n", in->len, in->s);
      goto error;

} name.len = p - name.s; trim(&name);

if(name.len==4 && strncasecmp(name.s, "user", 4)==0) {

      t->subtype = TR_URI_USER;
      return p;

} (:sourceend:)
After we've decided what actually needs to be done and did all the parsing,the tr_eval_uri will be the one gets called at runtime and that actually evaluates our transformation and returns the new transformation output.
In our case, the function parses the provided string into a SIP URI, and then populates the output pv_value_t accordingly to the subtype provided : (:source lang=C -link -getcode :) /* make a PKG copy of the input */ _tr_uri.s = (char*)pkg_malloc((val->rs.len+1)*sizeof(char)); memcpy(_tr_uri.s, val->rs.s, val->rs.len); _tr_uri.s[_tr_uri.len] = '\0'; ... ... /* parse the input URI */ if(parse_uri(_tr_uri.s, _tr_uri.len, &_tr_parsed_uri)!=0) {

      LM_ERR("invalid uri [%.*s]\n", val->rs.len,val->rs.s);
      return -1;

} ... ... /* zero out the output val */ memset(val, 0, sizeof(pv_value_t)); /* the output pvar will be a string */ val->flags = PV_VAL_STR;

switch(subtype) {

      case TR_URI_USER:
            val->rs = (_tr_parsed_uri.user.s)?_tr_parsed_uri.user:_tr_empty;
            break;
April 19, 2014, at 02:45 PM by 79.112.212.8 -
Added line 941:
Added line 945:
April 19, 2014, at 02:44 PM by 79.112.212.8 -
Changed line 940 from:
to:

\\

Changed line 944 from:
to:

\\

April 19, 2014, at 02:44 PM by 79.112.212.8 -
Added lines 939-965:

(:sourceend:)

Note that transformations can be chained together, which will have an impact on the transformations C development

(:source lang=C -link -getcode :) $var(our_uri) = “sip:vlad@opensips.org”; xlog(“The username of our URI has $(var(our_uri){uri.user}{s.len}) characters \n”); (:sourceend:)
In our examples, uri and s are the so called classes of transformations, while user and len are the actual operations within the class.
We will further follow the implementation of the uri class of transformation, and then focusing on the user function.
Adding new classes of transformations is done in transformations.h , where , for our example's case, TR_URI was added. Then, each class of transformations should have it's own functions enumeration, which is our case is enum _tr_uri_subtype .
transformations.c holds the actual implementation of transformations. parse_transformation will get called when the script needs to evaluate a transformation. There, we should add the matching for our new class of transformations :

(:source lang=C -link -getcode :) else if(tclass.len==3 && strncasecmp(tclass.s, "uri", 3)==0) {

      t->type = TR_URI; 
      t->trf = tr_eval_uri; 
      s.s = p; s.len = in->s + in->len - p; 
      p0 = tr_parse_uri(&s, t); 
      if(p0==NULL) 
            goto error; 
      p = p0; 

}

April 19, 2014, at 02:40 PM by 79.112.212.8 -
Added lines 927-938:

(:sourceend:)

Adding Transformations

The so called transformations are methods operating directly on the OpenSIPS various pseudo-variables. A transformation takes as input the value of the provided pseudo-variable and processes it, outputing a 'transformed' version.
Transformation examples are the URI transformations ( working on a SIP URI, and they allow extracting various useful information from the URI, like the user , domain, various parameters, etc ), the S class of transformations ( work on generic strings, and provide various useful methods as fetching the length of the string, searching the first occurrence of a character inside the string, etc ), and many others – see http://www.opensips.org/Documentation/Script-Tran-1-10
(:source lang=C -link -getcode :)

  1. example of usage

$var(tutorial) = “OpenSIPSDevel”; xlog(“Our variable has $(var(tutorial){s.len}) characters \n”);

April 19, 2014, at 02:29 PM by 79.112.212.8 -
Changed line 871 from:

From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done :

to:

From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done. The fixup part will be invoked only once, at script parsing.

Added lines 897-927:


In action.c we will have to add the actual code that needs to get executed when the function gets called from the OpenSIPS script at runtime : (:source lang=C -link -getcode :) case XLOG_T:

      /* add helpers for tracing the script */
      script_trace("core", "xlog", msg, a->line) ;
      if (a->elem[1].u.data != NULL) {
            /* we have two parameters */ 

            /* do security checks for the types of the provided parameters, second param has to be a SCRIPTVAR model as we've coded in the fixup */
            if (a->elem[1].type != SCRIPTVAR_ELEM_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type); 
                  ret=E_BUG; 
                  break; 
            }
            /* log level should be a plaintext string */ 
            if (a->elem[0].type != STR_ST) { 
                  LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); 
                  ret=E_BUG; 
                  break; 
            }

            /* call our C code function implementing the desired actions */
            if (xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data) < 0) { 
                  LM_ALERT("Cannot print xlog debug message"); 
                  break;
            }
      } else {
            /* one parameter case */
      }

(:sourceend:)

April 19, 2014, at 02:23 PM by 79.112.212.8 -
Changed line 884 from:
            /* parse the format provided to xlog - we can have variables inside
to:
            /* parse the format provided to xlog - we can have variables inside */
April 19, 2014, at 02:23 PM by 79.112.212.8 -
Changed lines 871-896 from:

From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done :

to:

From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done : (:source lang=C -link -getcode :) case XLOG_T:

      s.s = (char*)t->elem[1].u.data; 
      if (s.s == NULL) { 
            /* commands have only one parameter */ 
            s.s = (char *)t->elem[0].u.data; 
            s.len = strlen(s.s); 
            if(s.len==0) { 
                  LM_ERR("param is empty string!\n"); 
                  return E_CFG; 
            } 

            /* parse the format provided to xlog - we can have variables inside
            if(pv_parse_format(&s ,&model) || model==NULL) { 
                  LM_ERR("wrong format [%s] for value param!\n", s.s); 
                  ret=E_BUG; 
                  goto error; 
            } 

            /* overwrite the data that will be passed to contain our new parsed model */
            t->elem[0].u.data = (void*)model; 
            t->elem[0].type = SCRIPTVAR_ELEM_ST; 
      } else {
            /* two parameters */

(:sourceend:)

April 19, 2014, at 02:17 PM by 79.112.212.8 -
Changed lines 850-871 from:
Adding a core function
to:
Adding a core function

In the following step by step tutorial, we will follow the implementation of the xlog core parameter, which is used to print debugging information to the logging facility.

Note that xlog can receive either a single parameter ( the string to be printed ), or two parameters ( the log level and then the string to be printed ).

First, the grammar will have to be extended. In cfg,y we have : (:source lang=C -link -getcode :) %token XLOG ... ... | XLOG LPAREN STRING RPAREN {

      mk_action1($$, XLOG_T, STR_ST, $3); }                             
 XLOG LPAREN STRING COMMA STRING RPAREN {                                        
      mk_action2($$, XLOG_T, STR_ST, STR_ST, $3, $5); } 

(:sourceend:)
Note the two different forms of xlog displayed above.
From the grammar, we will start building the actions. In route.c we will define the fixup part of the function, where all the sanity checks and parameter parsing should be done :

April 19, 2014, at 02:12 PM by 79.112.212.8 -
April 19, 2014, at 02:12 PM by 79.112.212.8 -
Changed lines 829-830 from:

\\Adding the variable here will make it visible in both the OpenSIPS core and the OpenSIPS modules. main.c will hold the actual variable :

to:


Adding the variable here will make it visible in both the OpenSIPS core and the OpenSIPS modules. main.c will hold the actual variable :

Added lines 835-849:


Next, we will have to modify the grammar in order to accept our new parameter. In cfg.y , first we will define our new token : (:source lang=C -link -getcode :) %token CHILDREN (:sourceend:)
And then we'll set the parsing rules for the new token (:source lang=C -link -getcode :) | CHILDREN EQUAL NUMBER { children_no=$3; } | CHILDREN EQUAL error { yyerror("number expected"); } (:sourceend:)
Our variable will have a numeric variable, anything else will trigger and error in parsing the OpenSIPS script.

April 19, 2014, at 02:10 PM by 79.112.212.8 -
Added lines 823-833:

In the following step by step tutorial, we will follow the implementation of the children core parameter, which is an integer controlling the number of OpenSIPS processes per UDP interface.
First of all, we will have to add the variable that will hold the value of our new core parameter.
In our case, in globals.h we have added (:source lang=C -link -getcode :) extern int children_no; (:sourceend:) \\Adding the variable here will make it visible in both the OpenSIPS core and the OpenSIPS modules. main.c will hold the actual variable : (:source lang=C -link -getcode :) /* Default value in case the parameter is not set from the script */ int children_no = 8; (:sourceend:)

April 19, 2014, at 02:07 PM by 79.112.212.8 -
Changed lines 814-823 from:

(:sourceend:)

to:

(:sourceend:)

Extending OpenSIPS core Config File

OpenSIPS uses flex and bison in order to parse the configuration file and then build the entire action tree that a SIP message will go through once it is read from network level.
When it comes to extending the OpenSIPS configuration file directly in the core, the developer can either choose to add a new core parameter, or a new core function.

Adding a core parameter
Adding a core function
April 19, 2014, at 02:01 PM by 79.112.212.8 -
Changed lines 767-771 from:

Important to note here that all the above timer related functions MUST be called for the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ). \\

to:

Important to note here that all the above timer related functions MUST be called for the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).

April 19, 2014, at 02:01 PM by 79.112.212.8 -
Added lines 204-205:
Added lines 207-208:
April 19, 2014, at 02:00 PM by 79.112.212.8 -
Changed lines 144-148 from:

Important to note that the parse_headers() function will not duplicate SIP headers at all – the hooks in the struct sip_msg structure will be populated with pointers that point directly in the SIP message buffer.

to:

The parse_headers() function will not duplicate SIP headers at all – the hooks in the struct sip_msg structure will be populated with pointers that point directly in the SIP message buffer.

April 19, 2014, at 01:59 PM by 79.112.212.8 -
Added line 35:
Added line 39:
April 19, 2014, at 01:58 PM by 79.112.212.8 -
Changed lines 35-37 from:

Also, note the common use case where , before forking OpenSIPS processes, the developer stores some static variables in private memory of the main process. After forking, all the child processes will inherit that private memory chunk and each will have it's individual copy of it.

to:

Note the common use case where , before forking OpenSIPS processes, the developer stores some static variables in private memory of the main process. After forking, all the child processes will inherit that private memory chunk and each will have it's individual copy of it.

April 19, 2014, at 01:54 PM by 79.112.212.8 -
Changed lines 680-682 from:

\\

to:
Global Timer Process
Changed lines 718-720 from:

\\

to:
Dedicated Timer Process
April 19, 2014, at 01:52 PM by 79.112.212.8 -
Added lines 780-794:


Also, the OpenSIPS API also exposes the number of seconds and microseconds passed from the OpenSIPS start time. These can be accessed by calling (:source lang=C -link -getcode :) /* Returns :

      the number of seconds elapsed from OpenSIPS start
  • /

unsigned int get_ticks(void);

/* Returns:

      the number of microseconds elapsed from OpenSIPS start
  • /

utime_t get_uticks(void); (:sourceend:)

April 19, 2014, at 01:49 PM by 79.112.212.8 -
Changed lines 732-733 from:
                         unsigned int interval, unsigned int flags);

(:sourceend:)

to:
      unsigned int interval, unsigned int flags);

(:sourceend:)
The output struct sr_timer_process pointer can be further use to group together multiple functions inside the same process, by calling : (:source lang=C -link -getcode :) /* Parameters:

      label – opaque string containing a short timer function description ( to be used for logging )
      f – the actual function to be called
      param – parameter to be provided to the timer function
      interval – the interval, in seconds, that the function needs to be called at
      timer – the  struct sr_timer_process pointer obtained by previously calling  register_timer_process

Returns:

      0 in case of success, negative code in case of internal error.
  • /

int append_timer_to_process( char *label, timer_function f, void* param,

      unsigned int interval, void *timer);

(:sourceend:)
Important to note here that all the above timer related functions MUST be called for the context of the attendant process before forking is done ( so either from the modules mod_init or directly from the core, before forking ).
Below is a code snippet exemplifying how the dialog module's code used for registering two timers, with an option to either use the global timer process or to have it's own separate timer : (:source lang=C -link -getcode :) if (dlg_have_own_timer_proc) {

      LM_INFO("Running with dedicated dialog timer process\n"); 
      dlg_own_timer_proc = register_timer_process( "dlg-timer", 
            dlg_timer_routine, NULL,1,TIMER_PROC_INIT_FLAG ); 
      if (dlg_own_timer_proc == NULL) { 
            LM_ERR("Failed to init dialog own timer proc\n"); 
            return -1; 
      } 
      if (append_timer_to_process("dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval,dlg_own_timer_proc) < 0) { 
            LM_ERR("Failed to append ping timer \n"); 
            return -1; 
      }

} else {

      if ( register_timer( "dlg-timer", dlg_timer_routine, NULL, 1)<0 ) { 
            LM_ERR("failed to register timer \n"); 
            return -1; 
      } 

      if ( register_timer( "dlg-pinger", dlg_ping_routine, NULL, 
            ping_interval)<0) { 
            LM_ERR("failed to register timer 2 \n"); 
            return -1; 
      } 

} (:sourceend:)

April 19, 2014, at 11:56 AM by 79.112.212.8 -
Changed lines 716-733 from:
to:


Since, by default, all the registered timer functions are called from within the same process context, in case you are writing a timer process that is doing I/O, it is better to register an entirely new process where to run your code, since your function might slow down all the other timer functions running in OpenSIPS.
Registering a new timer process can be done by calling :

(:source lang=C -link -getcode :) /* Parameters :

      label - opaque string containing a short timer function description ( to be used for logging )
      f - the actual function to be called
      param - parameter to be provided to the timer function
      interval - the interval, in seconds, that the function needs to be called at
      flags - flags controlling process behavior. Currently only option is TIMER_PROC_INIT_FLAG , which leads to child_init to be called in the new timer process context. To be used when inside the timer you need to operate various I/O options which generally require a per process connection.

Returns :

      struct sr_timer_process pointer in case of success, or NULL in case of error.
  • /

void* register_timer_process(char *label, timer_function f, void* param,

                         unsigned int interval, unsigned int flags);

(:sourceend:)

April 19, 2014, at 11:53 AM by 79.112.212.8 -
Added line 683:

/*

Changed line 691 from:
to:
  • /
Changed line 700 from:
	typedef void (timer_function)(unsigned int ticks, void* param); 
to:

typedef void (timer_function)(unsigned int ticks, void* param);

April 19, 2014, at 11:52 AM by 79.112.212.8 -
Changed lines 673-715 from:

(:sourceend:)

to:

(:sourceend:)

Timer API

OpenSIPS exposes it's own API for implementing timer functions, with seconds and microsecond precision.
The OpenSIPS timer architecture is made out of the so called 'timer keeper' , which is a process which just increments global counter of second and microsecond precision, and one process which will execute the various timer functions when their time to execute has arrived.
Also, the OpenSIPS timers support automatic re-calibration in order to compensate timer drifting in case the timer executed functions do not complete in timely manners.
timer.h exposes all the relevant functionalities for operating the OpenSIPS timers. For registering a new timer function with second precision, use : (:source lang=C -link -getcode :) Parameters :

	label – opaque string containing a short timer function description ( to be used for logging )
	f – the actual function to be called
	param – parameter to be provided to the timer function
	interval – the interval, in seconds, that the function needs to be called at

Returns :

	0 in case of success, negative code in case of internal error.

int register_timer(char *label, timer_function f, void* param,

      unsigned int interval);

/* The seconds callback Parameters :

      ticks - represents the current number of seconds since OpenSIPS startup when the callback is called at
      param - is the parameter provided at timer function registration.
  • / typedef void (timer_function)(unsigned int ticks, void* param);

(:sourceend:)
For registering a microsecond timer, you should use (:source lang=C -link -getcode :) /* Parameters :

	label – opaque string containing a short timer function description ( to be used for logging )
	f – the actual function to be called
	param – parameter to be provided to the timer function
	interval – the interval, in microseconds, that the function needs to be called at

Returns :

	0 in case of success, negative code in case of internal error.
  • /

int register_utimer(char *label, utimer_function f, void* param, unsigned int interval); (:sourceend:)

April 19, 2014, at 12:34 AM by 79.112.212.8 -
Added line 2:

This page has been visited 25546 times.

Deleted line 3:

This page has been visited 25546 times.

April 19, 2014, at 12:33 AM by 79.112.212.8 -
Added line 3:

This page has been visited 25546 times.

April 19, 2014, at 12:21 AM by 79.112.212.8 -
Changed line 611 from:

void lock_destroy(gen_lock_set_t *lock);

to:

void lock_set_destroy(gen_lock_set_t *lock);

April 19, 2014, at 12:19 AM by 79.112.212.8 -
Changed lines 557-672 from:
Lock Set API
to:
Lock Set API

Operating on an entire array of locks can become very useful when dealing with structures like hashes, where you would need a lock per each hash entry.
The API can be used by including locking.h . The OpenSIPS generic array of locks is defined by the gen_lock_set_t structure, and working with it is very similar in concept to operating a single lock entry.
Allocating a new lock set is done by calling lock_set_alloc : (:source lang=C -link -getcode :) /* Returns :

      A shared memory allocated lock set, or NULL in case of an error.
  • /

gen_lock_set_t *lock_set_alloc(void); (:sourceend:)
Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock set separately - if the lock set is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock set, it must be initialized : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock set instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_set_t* lock_set_init(gen_lock_set_t* lock); (:sourceend:)
In order to acquire a lock in a lock set, one must use the lock_set_get function : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
      entry - the entry in the lock set that needs to be acquired
  • /

void lock_set_get(gen_lock_set_t *lock,int entry); (:sourceend:)
The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_set_release should be used : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be released.
      entry - the entry in the lock set that needs to be released
  • /

void lock_set_release(gen_lock_set_t *lock,int entry); (:sourceend:)
Once a lock set is no longer needed, one must first destroy the lock set, and then the lock set can be safely deallocated. (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock set to be destroyed
  • /

void lock_destroy(gen_lock_set_t *lock); /* Parameters :

      lock - the lock set to be deallocated
  • /

void lock_set_dealloc(gen_lock_set_t *lock); (:sourceend:)
Here is a code snippet showing the typical code used when dealing with single lock instances : (:source lang=C -link -getcode :) gen_lock_set_t *my_lock;

int init_function(void) {

	/* … */

        /* allocate lock set with 32 entries */
	my_lock = lock_set_alloc(32);
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock set \n”);
		return -1;
	}

	if (lock_set_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock set \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */

	/* acquire entry 5 in the lock set */
	lock_set_get(my_lock,5)

	/* also acquire entry 21 in the lock set */
	lock_set_get(my_lock,21);

	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_set_release(my_lock,21);
	lock_set_release(my_lock,5);
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_set_destroy(my_lock);
	lock_set_dealloc(my_lock);

	/* … */

} (:sourceend:)

April 19, 2014, at 12:13 AM by 79.112.212.8 -
Changed line 557 from:

Lock Set API

to:
Lock Set API
April 19, 2014, at 12:13 AM by 79.112.212.8 -
Changed lines 442-557 from:

Locking API

to:

Locking API

OpenSIPS has it's own locking API, and it is recommended to use it instead of the system exposed locks, since they offer greater flexibility - depending on the usage case and the menuconfig provided compilation flags, the OpenSIPS generic locks can be converted either to busy locks, futexes, SysV locks, etc.
The Locking API offers two distinct functionalities, one for using single lock elements, and another for operating on entire sets of locks.

Single Lock API

The API can be used by including “locking.h” . The OpenSIPS generic lock is defined by the gen_lock_t structure.
Allocating a new lock is done by calling lock_alloc : (:source lang=C -link -getcode :) /* Returns :

      A shared memory allocated lock, or NULL in case of an error.
  • /

gen_lock_t *lock_alloc(void); (:sourceend:)
Since the locks usually have to be reachable by all processes fighting on some resource, the locks are allocated by default in shared memory. Also, note that it is not necessary to always allocate the lock separately - if the lock is embedded within a structure which is allocated in SHM, the effect is the same.
Before any operation on the lock, it must be initialized : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock instance to be initialized

Returns :

      The initialized lock in case of success, or NULL in case of error.
  • /

gen_lock_t* lock_init(gen_lock_t* lock); (:sourceend:)
In order to acquire a lock, one must use the lock_get function : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be acquired
  • /

void lock_get(gen_lock_t *lock); (:sourceend:)
The function will block if the lock is acquired by another process, and will only return once the lock has been acquired by the current process.
For releasing a lock, lock_release should be used : (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be released.
  • /

void lock_release(gen_lock_t *lock); (:sourceend:)
Once a lock is no longer needed, one must first destroy the lock, and then the lock can be safely deallocated. (:source lang=C -link -getcode :) /* Parameters :

      lock - the lock to be destroyed
  • /

void lock_destroy(gen_lock_t *lock); /* Parameters :

      lock - the lock to be deallocated
  • /

void lock_dealloc(gen_lock_t *lock); (:sourceend:)
Here is a code snippet showing the typical code used when dealing with single lock instances : (:source lang=C -link -getcode :)

gen_lock_t *my_lock;

int init_function(void) {

	/* … */

	my_lock = lock_alloc();
	if (my_lock == NULL) {
		LM_ERR(“Failed to allocate lock \n”);
		return -1;
	}

	if (lock_init(my_lock) == NULL) {
		LM_ERR(“Failed to init lock \n”);
		return -1;
	}

	/* … */
	return 0;

}

int do_work(void) {

	/* … */
	lock_get(my_lock)


	/* critical region protected by our lock
	generally recommended to keep critical regions short
	I/O operations to be avoided in such critical regions */

	lock_release(my_lock)
	/* … */

}

void destroy_function(void) {

	/* … */

	lock_destroy(my_lock);
	lock_dealloc(my_lock);

	/* … */

} (:sourceend:)

Lock Set API

April 19, 2014, at 12:04 AM by 79.112.212.8 -
Changed lines 440-442 from:

(:sourceend:)

to:

(:sourceend:)

Locking API

April 18, 2014, at 11:07 PM by 79.112.212.8 -
Added line 343:
  • /
Added lines 426-427:
  • /

struct lump_rpl* add_lump_rpl(struct sip_msg *msg, char *s, int len, int flags);

April 18, 2014, at 11:06 PM by 79.112.212.8 -
Changed lines 411-437 from:
SIP Reply Lumps
to:
SIP Reply Lumps

When used in the case of a SIP request, these lumps will operate on the SIP reply that will be internally generated when rejecting a request from within OpenSIPS ( if the Request if forwarded instead of rejected at OpenSIPS level, these lumps will have no effect ). Since the reply will be internally generated by OpenSIPS, the Reply Lumps can only add new content.
data_lump_rpl.h exposes (:source lang=C -link -getcode :) /* Parameters :

      msg - the SIP Request that the reply will be generated for
      s - the string to be added to the reply
      len - the length of the string to be added
      flags - Since the reply will be generated by OpenSIPS, it is important to mark your lump if it should be added to the Reply headers or to the Reply body. Relevant flags for these cases are LUMP_RPL_HDR and LUMP_RPL_BODY.

Returns :

      the created lump structure for adding to the SIP reply. Can be further used to chain together lumps in the message attached list of lumps. NULL is returned in case of internal error.

(:sourceend:)
Example of Adding contact header to the internally generated reply : (:source lang=C -link -getcode :)

      static char ct[CT_LEN] = “Contact: opensips@my_domain.com\r\n”;

      /* we are adding a lump to the headers, so we pass the  LUMP_RPL_HDR flag
      also , our buffer is located in a static buffer, thus no need for the core to allocate memory for this lump, we also pass the LUMP_RPL_NODUP flag */
      if (add_lump_rpl(msg, ct, CT_LEN, LUMP_RPL_HDR |  LUMP_RPL_NODUP)==0) { 
            LM_ERR("unable to add lump\n"); 
            return -1; 
      } 

(:sourceend:)

April 18, 2014, at 11:01 PM by 79.112.212.8 -
Added lines 379-409:

If we want to replace a particular part of a SIP message, the operation can be split in two steps, first deleting the part we don't need anymore by calling del_lump, and then using the returned lump to add a new lump after it.
Example of replacing the content of the RPID header : (:source lang=C -link -getcode :)

      /* first parse the header to figure out where it actually starts in the SIP message */
      if( parse_headers(msg,HDR_RPID_F,0)<0 || msg->rpid == NULL ){ 
            LM_DBG(“No rpid header – nothing to delete \n”);
	    return 0;
      }

      /* delete just the contents of the RPID header */
      del =  del_lump(msg, msg->rpid->body.s-msg->buf, msg->rpid->body.len,HDR_RPID_T);
      if ( del == NULL) {
            LM_ERR(“Failed to delete RPID header \n”);
	    return -1;
      }

      len =  sizeof(“sip:new_rpid@my_domain.com\r\n”) -1;
      new_rpid=pkg_malloc(len);
      if (!new_rpid) {
            LM_ERR(“No more pkg mem\n”);
	    return -1;
      }
      memcpy(new_rpid,“sip:new_rpid@my_domain.com\r\n”,len);

      if(insert_new_lump_after(del,new_rpid,len,HDR_RPID_T)==NULL) { 
            LM_ERR("Failed to insert new callid\n"); 
            pkg_free(new_rpid); 
            return -1; 
      } 

(:sourceend:)

April 18, 2014, at 10:57 PM by 79.112.212.8 -
Added lines 312-378:

data_lump.h exposes

(:source lang=C -link -getcode :) /* Parameters :

      after/before - the lump where we will connect our new lump
      new_hdr - string to be added
      len - length of the string to be added
      type - header type that is affected by the current change ( can be 0 )

Returns :

      the created lump structure for adding to the SIP message. Can be further used to chain together different types of lumps in the message attached list of lumps. NULL is returned in case of internal error.
  • /

struct lump* insert_new_lump_after(struct lump* after,

                char* new_hdr, unsigned int len, enum _hdr_types_t type);                                        

struct lump* insert_new_lump_before(struct lump* before, char* new_hdr,

                unsigned int len,enum _hdr_types_t type);

(:sourceend:)
If the developer's desire is just to add a particular string to the SIP message, a new anchor lump must be created, that will then have to be provided as the first parameter to insert_new_lump_after/insert_new_lump_before.
For creating a new anchor lump, data_lump.h also exports (:source lang=C -link -getcode :) /* Parameters :

      msg - the SIP message that will be affected by the lump anchor
      offset - the offset in the SIP message where the anchor will be placed
      len - not currently used ( should be 0 )
      type - header type that is affected by the current change ( can be 0 )

Returns:

      the created lump structure for adding to the SIP message. Can be further used to chain together different types of lumps in the message attached list of lumps. NULL is returned in case of internal error.

struct lump* anchor_lump(struct sip_msg* msg, unsigned int offset,

                int unsigned len, enum _hdr_types_t type) 

(:sourceend:)
Example of adding a new SIP header at the end of the SIP message headers : (:source lang=C -link -getcode :)

      /* make sure we detect all headers */
      if (parse_headers(msg, HDR_EOH_F, 0) == -1) { 
            LM_ERR("error while parsing message\n"); 
            return -1; 
      } 

      /* add the anchor at the very end of the SIP headers */
      anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
      if (anchor == NULL) {
            LM_ERR(“Failed to create lump anchor\n”);
	    return -1;
      }
      len =  sizeof(“MY_HDR: MY_VAL\r\n”) -1;
      new_hdr=pkg_malloc(len);
      if (!new_hdr) {
            LM_ERR(“No more pkg mem\n”);
	    return -1;
      }

      memcpy(new_hdr,”MY_HDR: MY_VAL\r\n”,len);
      if (insert_new_lump_after(anchor, new_hdr, len, 0) == 0) { 
            LM_ERR("can't insert lump\n"); 
            pkg_free(new_hdr); 
            return -1; 
      } 

      /* job done, the PKG new_hdr mem will be free internally when the lump will be applied */
      return 0;

(:sourceend:)

April 18, 2014, at 10:51 PM by 79.112.212.8 -
Added lines 279-309:

data_lump.h exposes

(:source lang=C -link -getcode :) /* Parameters :

      msg - the SIP message the lump will affect
      offset - the offset in the SIP message at which to start deleting
      len - the number of characters to delete from the SIP message
      type - indication on which header the current lump affects ( can be 0 )

Returns :

      the created lump structure for deleting part of the SIP message. Can be further used to chain together different types of lumps in the message attached list of lumps. NULL is returned in case of internal error.
  • /

struct lump* del_lump(struct sip_msg* msg, unsigned int offset,

        unsigned int len, enum _hdr_types_t type); 

(:sourceend:)
Example of deleting the RPID header : (:source lang=C -link -getcode :)

      /* first parse the header to figure out where it actually starts in the SIP message */
      if( parse_headers(msg,HDR_RPID_F,0)<0 || msg->rpid == NULL ){ 
            LM_DBG(“No rpid header – nothing to delete \n”);
	    return 0;
      }

      /* delete the entire RPID header */
      if ( del_lump(msg, msg->rpid->name.s-msg->buf, msg->rpid->len,HDR_RPID_T )== NULL) {
            LM_ERR(“Failed to delete RPID header \n”);
	    return -1;
      }

(:sourceend:)

April 18, 2014, at 10:46 PM by 79.112.212.8 -
Added lines 273-280:

This type of lumps operate on the current SIP message context.
From operational point of view, they are also split into two categories :

Delete Lumps
Add Lumps
April 18, 2014, at 08:47 PM by 213.233.84.113 -
Changed line 263 from:

The function will internally populate _m->sdp , see parser/sdp/sdp.h for more details on the sdp_info structure.

to:

The function will internally populate _m->sdp , see parser/sdp/sdp.h for more details on the sdp_info structure.

April 18, 2014, at 08:46 PM by 213.233.84.113 -
Changed line 127 from:
to:

\\

Changed line 129 from:
to:

\\

Deleted lines 135-136:

HDR_EOH_F is exposed in case the developer wants to parse all the headers in the current SIP message.

Added lines 137-138:

HDR_EOH_F is exposed in case the developer wants to parse all the headers in the current SIP message. \\

Deleted lines 193-194:

Note that the void *parsed element in the hdr_field structure will contain the header specific parser structure, which will also be allocated into private memory and automatically freed when the SIP message processing has finished.

Added lines 195-196:

Note that the void *parsed element in the hdr_field structure will contain the header specific parser structure, which will also be allocated into private memory and automatically freed when the SIP message processing has finished. \\

Deleted lines 218-219:

The parse_uri() function does not allocate any memory, it just populates the sip_uri structure with references to the buf parameter provided as input.

Added lines 220-221:

The parse_uri() function does not allocate any memory, it just populates the sip_uri structure with references to the buf parameter provided as input. \\

Changed line 251 from:
to:

\\

April 18, 2014, at 08:45 PM by 213.233.84.113 -
Changed line 262 from:
to:

\\

April 18, 2014, at 08:44 PM by 213.233.84.113 -
Added lines 235-264:
Parsing the SDP Body

OpenSIPS exposes functions for operating on the SIP message body.
parser/msg_parser.h exposes (:source lang=C -link -getcode :) /* Parameters :

      msg - the SIP message to fetch the body for
      body - output param, which will hold the body pointer inside the SIP message and the body length, or {NULL,0} in case of no body present

Returns :

      0 in case of success, or -1 in the case of parsing errors ( the function needs to internally parse all the headers in order to detect the body length ).
  • /

int get_body(struct sip_msg *msg, str *body) (:sourceend:)

For parsing the SDP body and extracting various information about the session(s) , parser/sdp/sdp.h exposes (:source lang=C -link -getcode :) /* Parameters :

      _m - the SIP message to have it's SDP parsed

Returns :

      0 in case of success, negative in case of error
  • /

int parse_sdp(struct sip_msg* _m); (:sourceend:)

The function will internally populate _m->sdp , see parser/sdp/sdp.h for more details on the sdp_info structure.

April 18, 2014, at 08:39 PM by 213.233.84.113 -
Added lines 203-234:
Parsing SIP URIs

The OpenSIPS parser also exposes the functionality of parsing individual SIP URI.
parser/parse_uri.h exposes :

(:source lang=C -link -getcode :) /* Parameters :

      buf - the string which contains our SIP URI
      len - length of the SIP URI buffer
      uri - structure which will be populated by the function in case of success. 
      See full struct sip_uri members in parser/msg_parser.h

Returns :

      0 in case of success, negative value in case of error parsing the URI

int parse_uri(char *buf, int len, struct sip_uri* uri); (:sourceend:)

The parse_uri() function does not allocate any memory, it just populates the sip_uri structure with references to the buf parameter provided as input.
Here is an example of parsing the TO URI : (:source lang=C -link -getcode :)

      /* make sure TO header is parsed before this */
      struct to_body *tb = get_to(msg);
      if (parse_uri(tb->uri.s, tb->uri.len , &tb->parsed_uri)<0) { 
            LM_ERR("failed to parse To uri\n"); 
  	    return -1;
      }

      LM_INFO(“TO URI user is %.*s and TO URI domain is %.*s\n”,
      tb->parsed_uri.user.len, tb->parsed_uri.user.s,
      tb->parsed_uri.domain.len, tb->parsed_uri.domain.s);

(:sourceend:)

April 18, 2014, at 08:34 PM by 213.233.84.113 -
Changed line 195 from:

Note that the void *parsed element in the hdr_field structure will contain the header specific parser structure, which will also be allocated into private memory and automatically freed when the SIP message processing has finished.

to:

Note that the void *parsed element in the hdr_field structure will contain the header specific parser structure, which will also be allocated into private memory and automatically freed when the SIP message processing has finished.

April 18, 2014, at 08:34 PM by 213.233.84.113 -
Added lines 194-202:

Note that the void *parsed element in the hdr_field structure will contain the header specific parser structure, which will also be allocated into private memory and automatically freed when the SIP message processing has finished.
After parse_to_header() successfully returns, the developer can start accessing the parsed TO header in the following way :

(:source lang=C -link -getcode :) LM_INFO(“The TO header tag value is %.*s \n”,get_to(msg)->tag_value.len, get_to(msg)->tag_value.s); (:sourceend:)

April 18, 2014, at 08:32 PM by 213.233.84.113 -
Added lines 111-112:
Generic Header Parser
Added lines 148-149:
Specific Header Parsing
April 18, 2014, at 08:30 PM by 213.233.84.113 -
Added line 142:
Added lines 145-188:

(:sourceend:)

For parsing a specific header type, and extracting the header type relevant information, the parser/ folder contains all implementations for known headers. The naming convention is that parser/parse_X.h will expose the parsing for the X header name.
For example, here is the implementation of the parsing of the TO header, exposed by parser/parse_to.h :

(:source lang=C -link -getcode :) int parse_to_header( struct sip_msg *msg) {

      struct to_body* to_b; 

      if ( !msg->to && ( parse_headers(msg,HDR_TO_F,0)==-1 || !msg->to)) { 
            LM_ERR("bad msg or missing To header\n"); 
            goto error; 
      } 

      /* maybe the header is already parsed! */ 
      if (msg->to->parsed) 
            return 0; 

      /* bad luck! :-( - we have to parse it */ 
      /* first, get some memory */ 
      to_b = pkg_malloc(sizeof(struct to_body)); 
      if (to_b == 0) { 
            LM_ERR("out of pkg_memory\n"); 
            goto error; 
      } 

      /* now parse it!! */ 
      memset(to_b, 0, sizeof(struct to_body)); 
      parse_to(msg->to->body.s,msg->to->body.s+msg->to->body.len+1,to_b); 
      if (to_b->error == PARSE_ERROR) { 
            LM_ERR("bad to header\n"); 
            pkg_free(to_b); 
            goto error; 
      } 

      msg->to->parsed = to_b; 

      return 0; 

error:

      return -1; 

}

April 18, 2014, at 08:25 PM by 213.233.84.113 -
Added lines 135-144:

HDR_EOH_F is exposed in case the developer wants to parse all the headers in the current SIP message.
Important to note that the parse_headers() function will not duplicate SIP headers at all – the hooks in the struct sip_msg structure will be populated with pointers that point directly in the SIP message buffer.
Upon return, if successful, the function will populate the respective hooks in the sip_msg structure for known headers.The hdr_field* structures are allocated in pkg memory, and will automatically be freed when the SIP message processing has finished.
After succesful parsing, the developer can access the header name and body like this : (:source lang=C -link -getcode :) LM_INFO(“The callid header name is %.*s and the callid header body is %.*s \n”, req->callid->name.len, req->callid->name.s, req->callid->bodylen. req->callid->body.s); (:sourceend:)

April 18, 2014, at 08:21 PM by 213.233.84.113 -
April 18, 2014, at 08:21 PM by 213.233.84.113 -
Added lines 124-132:

(:sourceend:)

Example of usage :

(:source lang=C -link -getcode :) if(parse_headers(req,HDR_CALLID_F|HDR_TO_F|HDR_FROM_F,0)<0 || !req->callid || !req->to || !req->from) {

      LM_ERR("bad request or missing CALLID/TO/FROM hdr \n"); 
      return -1; 

}

April 18, 2014, at 08:19 PM by 213.233.84.113 -
Changed lines 96-126 from:

(:sourceend:)

to:

(:sourceend:)

Parsing SIP Messages

The OpenSIPS SIP message parser is a lazy parser, which performs very well in terms of performance. The behavior is the following :

  • when the message is received, only the 'critical' headers are parsed ( eg : topmost VIA )
  • for known header types, when the developer wants to extract the first occurrence of a header, it does not have to parse the entire message, but rather the parser will stop parsing when the first needed header type was identified


Also, important to note that there are two types of parsing :

  • parsing in the sense of identifying header boundaries and separating between the header name and header body ( eg : parsing the 'To : vlad@opensips.org;tag=123\r\n' header, and identifying that first, the header is present, and secondly that the body is 'vlad@opensips.org;tag=123\r\n' )
  • in-depth parsing of a specific header type, for identifying header specific information ( in our example, extracting the to-tag in the TO header, etc ).


Also, all parsing is done in a stateful manner, in the sense that the OpenSIPS parser know which header was parsed and by internally storing a bitmask. Thus, once the TO header was first parsed, any subsequent attempts of parsing the TO header will return immediately.
Parser implementations are generally exported in the parser/ folder. The generic parser of SIP headers is exposed by parser/msg_parser.h . The function to be used is :

(:source lang=C -link -getcode :) /* Parameters :

      msg : the SIP message that needs to be parsed – see parser/msg_parser.h for details on the struct sip_msg structure
      flags : bitmask of header types that need to be parsed
      next : specifies whether the parser should explicitly force the parsing of new headers from the provided bitmask, even though those header types were already previously found. Can be useful when trying to find a second occurrence of a header ( in the case that header can appear multiple times in a SIP message – eg. Route )

Returns :

      0 in case of error, -1 in case of error ( either header was not found or some other error occurred ).
  • /

int parse_headers(struct sip_msg* msg, hdr_flags_t flags, int next); (:sourceend:)

Changing SIP Messages

April 18, 2014, at 08:14 PM by 213.233.84.113 -
Changed lines 67-96 from:

Also, since allocations of chunks of shared memory can be requested from all the OpenSIPS processes, the shared memory allocator internally uses locking in order to ensure consistency. Due to this, as a general guideline, it is recommended to avoid big fragmentation of the shared memory ( by requesting many small chunks ), by merging as much information that is meaningfully tied together in a single shared memory chunk.

to:

Also, since allocations of chunks of shared memory can be requested from all the OpenSIPS processes, the shared memory allocator internally uses locking in order to ensure consistency. Due to this, as a general guideline, it is recommended to avoid big fragmentation of the shared memory ( by requesting many small chunks ), by merging as much information that is meaningfully tied together in a single shared memory chunk.

mem/shm_mem.h exposes all the shared memory related functions :

(:source lang=C -link -getcode :) /* Parameters :

      size - size in bytes of the request shared memory

Returns :

      the actual allocated buffer, or NULL is case of error
  • /

void *shm_malloc(unsigned int size);

/* Parameters :

      buf - the buffer to be freed
  • /

void shm_free(void *buf)

/* Parameters :

      buf - buffer that we want to reallocate
      size - the new desired buffer size

Returns :

	the new buffer address if reallocation is successful, or NULL in case of error. 

Note that shm_realloc(NULL,size) is equivalent to shm_malloc(size)

  • /

void *shm_realloc(void *buf, unsigned int size); (:sourceend:)

April 18, 2014, at 08:11 PM by 213.233.84.113 -
Changed line 66 from:

Shared memory is and can be accessible from all OpenSIPS processes. Thus, generally speaking, all write access to a shared memory buffer should be guarded by some form of synchronization mechanism in order to ensure consistency.\\

to:

Shared memory can be accessible from all OpenSIPS processes. Thus, generally speaking, all write access to a shared memory buffer should be guarded by some form of synchronization mechanism in order to ensure consistency.\\

April 18, 2014, at 08:11 PM by 213.233.84.113 -
Changed line 28 from:

\\\

to:

\\

April 18, 2014, at 08:10 PM by 213.233.84.113 -
Changed lines 65-67 from:
Shared (SHM) Memory
to:
Shared (SHM) Memory

Shared memory is and can be accessible from all OpenSIPS processes. Thus, generally speaking, all write access to a shared memory buffer should be guarded by some form of synchronization mechanism in order to ensure consistency.
Also, since allocations of chunks of shared memory can be requested from all the OpenSIPS processes, the shared memory allocator internally uses locking in order to ensure consistency. Due to this, as a general guideline, it is recommended to avoid big fragmentation of the shared memory ( by requesting many small chunks ), by merging as much information that is meaningfully tied together in a single shared memory chunk.

April 18, 2014, at 08:09 PM by 213.233.84.113 -
Changed line 41 from:
      size – size in bytes of the request private memory
to:
      size - size in bytes of the request private memory
Added lines 46-62:

/* Parameters :

      buf - the buffer to be freed
  • /

void pkg_free(void *buf)

/* Parameters :

      buf - buffer that we want to reallocate
      size - the new desired buffer size

Returns :

	the new buffer address if reallocation is successful, or NULL in case of error. 

Note that pkg_realloc(NULL,size) is equivalent to pkg_malloc(size)

  • /

void *pkg_realloc(void *buf, unsigned int size);

April 18, 2014, at 08:07 PM by 213.233.84.113 -
Changed lines 39-40 from:

/* Parameters :

to:

/* Parameters :

April 18, 2014, at 08:07 PM by 213.233.84.113 -
Changed lines 39-43 from:

/* wtf */

to:

/* Parameters :

      size – size in bytes of the request private memory

Returns :

      the actual allocated buffer, or NULL is case of error
  • /
April 18, 2014, at 08:06 PM by 213.233.84.113 -
Added lines 36-37:

mem/mem.h exposes all the private memory related functions

Added line 39:

/* wtf */

April 18, 2014, at 08:04 PM by 213.233.84.113 -
Changed lines 28-29 from:

\\

to:



April 18, 2014, at 08:03 PM by 213.233.84.113 -
Changed line 22 from:

\\\

to:

\\

Changed line 24 from:

\\\

to:

\\

Changed line 26 from:

\\\

to:

\\

Changed line 28 from:

\\\

to:

\\

Changed line 32 from:

\\\

to:

\\

Changed line 34 from:

\\\

to:

\\

April 18, 2014, at 08:02 PM by 213.233.84.113 -
Changed lines 22-23 from:


to:

\\\

Changed lines 24-25 from:


to:

\\\

Changed lines 26-27 from:


to:

\\\

Changed lines 28-29 from:


to:

\\\

Changed line 32 from:

\\

to:

\\\

Changed line 34 from:
to:

\\\

April 18, 2014, at 08:01 PM by 213.233.84.113 -
Added line 23:
Added line 26:
Added line 29:
April 18, 2014, at 08:00 PM by 213.233.84.113 -
Deleted lines 17-18:
Private (PKG) Memory
Changed line 22 from:
to:

\\

Changed line 24 from:
to:

\\

Changed line 26 from:
to:

\\

Added lines 28-34:


Private (PKG) Memory

Private memory is only specific to a single OpenSIPS process. Since it has no visibility outside the current process, no locking mechanisms are required while using such memory.
Also, note the common use case where , before forking OpenSIPS processes, the developer stores some static variables in private memory of the main process. After forking, all the child processes will inherit that private memory chunk and each will have it's individual copy of it.

April 18, 2014, at 07:57 PM by 213.233.84.113 -
Changed line 31 from:

(:source lang=C -link :)

to:

(:source lang=C -link -getcode :)

April 18, 2014, at 07:56 PM by 213.233.84.113 -
Changed line 31 from:

(:source lang=C :)

to:

(:source lang=C -link :)

April 18, 2014, at 07:55 PM by 213.233.84.113 -
Added lines 30-33:

(:source lang=C :) void *pkg_malloc(unsigned int size); (:sourceend:)

April 18, 2014, at 07:45 PM by 213.233.84.113 -
Added lines 19-29:

OpenSIPS has it's own memory allocator, and this provides some important advantages over the system memory allocator :

  • the ability to hard limit the memory that OpenSIPS uses
  • improved performance over the system memory allocator
  • multiple allocators are available, each with different use cases ( options are the standard allocator, the memory debugging allocator – useful for debugging memory leaks and memory corruptions – and some others , check the menuconfig configurations options ).

Furthermore, since OpenSIPS is a multi-process application, the use of shared memory is required in many very common scenarios. The OpenSIPS allocators also hide the shared memory implementations details, and expose a very simple API, very similar to the system allocator.

All OpenSIPS allocators request from the system memory, at startup, the maximum configured memory for OpenSIPS, and then it manages it internally as OpenSIPS starts to use memory while processing traffic.

From OpenSIPS perspective, there are two types of memory, one that is related to the context of a single Process ( Private Memory ) , and another that is shared between all OpenSIPS processes ( Shared Memory ).

April 18, 2014, at 07:44 PM by 213.233.84.113 -
Added lines 11-20:

Introduction

General Architecture

Memory Management

Private (PKG) Memory
Shared (SHM) Memory
April 18, 2014, at 07:36 PM by 213.233.84.113 -
Added lines 1-10:
Documentation -> Manuals -> OpenSIPS Development Manual


OpenSIPS Development Manual

(:toc-float Table of Content:)


Page last modified on October 15, 2022, at 04:50 AM