<!-- cfgutils Module User's Guide -->

<chapter>
    
    <title>&adminguide;</title>

    <section id="overview" xreflabel="Overview">
	<title>Overview</title>
	<para>Useful extensions for the server configuration.</para>
	<para>
	The cfgutils module can be used to introduce randomness to
	the behaviour of the server. It provides setup functions
	and the <quote>rand_event</quote> function. This function return either
	true or false, depending on a random value and a specified probability.
	E.g. if you set via fifo or script a probability value of 5%, then 5% of
	all calls to rand_event will return false.
	The pseudovariable <quote>$RANDOM</quote> could be used to introduce 
	random values e.g. into a SIP reply.
	</para>
	<para>
	The benefit of this module is the probability of the decision
	can be manipulated by external applications such as web interface
	or command line tools. The probability must be specified as 
	percent value, ranging from 0 to 100.
	</para>
	<para>
	The module exports commands to FIFO server that can be used to change
	the global settings via FIFO interface. The FIFO commands are:
	<quote>set_prob</quote>, <quote>reset_prob</quote> and
	<quote>get_prob</quote>.
	</para>
	<para>
	This module can be used for simple load-shedding, e.g. reply 5% of
	the Invites with a 503 error and a adequate random Retry-After value.
	</para>
	<para>
	The module provides as well functions to delay the execution of the
	server. The functions <quote>sleep</quote> and <quote>usleep</quote> could
	be used to let the server wait a specific time interval.
	</para>
	<para>It can also hash the config file used from the server with a (weak)
	cryptographic hash function on startup. This value is saved and can be
	later compared to the actual hash, to detect modifications of this file
	after the server start. This functions are available as the FIFO commands
	<quote>check_config_hash</quote> and <quote>get_config_hash</quote>.
	</para>
	</section>
	<section id="dependencies" xreflabel="Dependencies">
	<title>Dependencies</title>
	<para>
	    The module depends on the following modules (in the other words the
		listed modules must be loaded before this module):
	    <itemizedlist>
		<listitem>
		    <para><emphasis>none</emphasis></para>
		</listitem>
	    </itemizedlist>
	</para>
    </section>
    <section id="exported_parameters" xreflabel="Exported Parameters">
	<title>Exported Parameters</title>

	<section id="param_initial_probability" xreflabel="initial_probability">
		<title><varname>initial_probability</varname> (string)</title>
		<para>
		The initial value of the probability.
		</para>
		<para>
		Default value is 
			<quote>10</quote>.
		</para>
		<example>
		<title><varname>initial_probability</varname> parameter usage</title>
		<programlisting format="linespecific">
   
modparam("cfgutils", "initial_probability", 15)
   
</programlisting>
	    </example>
	</section>

	<section id="param_hash_file" xreflabel="hash_file">
		<title><varname>hash_file</varname> (string)</title>
		<para>
		The config file name for that a hash value should be calculated on startup.
		</para>
		<para>
		There is no default value, is no parameter is given the hash functionality
		is disabled.
		</para>
		<example>
		<title><varname>hash_file</varname> parameter usage</title>
		<programlisting format="linespecific">
   
modparam("cfgutils", "hash_file", "/etc/opensips/opensips.cfg")
   
</programlisting>
	    </example>
	</section>

	<section id="param_shv_hash_size" xreflabel="shv_hash_size">
		<title><varname>shv_hash_size</varname> (integer)</title>
		<para>
		The size of the hash table used to store the shared variables ($shv).
		</para>
		<para>
		Default value is <quote>64</quote>.
		</para>
		<example>
		<title><varname>shv_hash_size</varname> parameter usage</title>
		<programlisting format="linespecific">

modparam("cfgutils", "shv_hash_size", 1024)

</programlisting>
	    </example>
	</section>

	<section id="param_shvset" xreflabel="shvset">
		<title><varname>shvset</varname> (string)</title>
		<para>
		Set the value of a shared variable ($shv(name)). The parameter
		can be set many times.
		</para>
		<para>
		The value of the parameter has the format:
		_name_ '=' _type_ ':' _value_
		</para>
		<itemizedlist>
			<listitem><para>_name_: shared variable name</para></listitem>
			
			<listitem><para>_type_: type of the value</para>
			      <itemizedlist>
	                 <listitem><para> <quote>i</quote>: integer value </para></listitem> 
			         <listitem><para> <quote>s</quote>: string value </para></listitem>	
				  </itemizedlist>
			</listitem>	  

			<listitem><para>_value_: value to be set</para></listitem>
		</itemizedlist>
		<para>
		Default value is <quote>NULL</quote>.
		</para>
		<example>
		<title><varname>shvset</varname> parameter usage</title>
		<programlisting format="linespecific">
...
modparam("cfgutils", "shvset", "debug=i:1")
modparam("cfgutils", "shvset", "pstngw=s:sip:10.10.10.10")
...
</programlisting>
	    </example>
	</section>

	<section id="param_varset" xreflabel="varset">
		<title><varname>varset</varname> (string)</title>
		<para>
		Set the value of a script variable ($var(name)). The parameter
		can be set many times.
		</para>
		<para>
		The value of the parameter has the format:
		_name_ '=' _type_ ':' _value_
		</para>
		<itemizedlist>
			<listitem><para>_name_: shared variable name</para></listitem>
			
			<listitem><para>_type_: type of the value</para>
			      <itemizedlist>
	                 <listitem><para> <quote>i</quote>: integer value </para></listitem> 
			         <listitem><para> <quote>s</quote>: string value </para></listitem>	
				  </itemizedlist>
			</listitem>	  

			<listitem><para>_value_: value to be set</para></listitem>
		</itemizedlist>
		<para>
		Default value is <quote>NULL</quote>.
		</para>
		<example>
		<title><varname>varset</varname> parameter usage</title>
		<programlisting format="linespecific">
...
modparam("cfgutils", "varset", "init=i:1")
modparam("cfgutils", "varset", "gw=s:sip:11.11.11.11;transport=tcp")
...
</programlisting>
	    </example>
	</section>

	<section id="param_lock_pool_size" xreflabel="lock_pool_size">
		<title><varname>lock_pool_size</varname> (integer)</title>
		<para>
		The number of dynamic script locks to be allocated at &osips; startup. This
		number must be a power of 2. (i.e. 1, 2, 4, 8, 16, 32, 64 ...)
		</para>
		<para>
		Note that the <emphasis>lock_pool_size</emphasis> parameter only affects
		the number of dynamic locks created at startup. The pool of static locks
		only depends on the number of unique static strings supplied throughout
		the script to the set of static lock functions.
		</para>
		<para>
		Default value is <quote>32</quote>.
		</para>
		<example>
		<title>Setting lock_pool_size module parameter</title>
		<programlisting format="linespecific">
modparam("cfgutils", "lock_pool_size", 64)
</programlisting>
		</example>
	</section>
</section>

    <section id="exported_functions" xreflabel="exported_functions">
	<title>Exported Functions</title>
	<section id="func_rand_event" xreflabel="rand_event()">
		<title><function moreinfo="none">rand_event([probability])</function></title>
		<para>
			Generates a random floating point value between 0 - 100 and returns
			true if the value is less or equal to the currently set probability.
			If "probability" parameter is given, it will
			override the global parameter set by <xref linkend="func_rand_set_prob"/>.
		</para>
		<para>Parameters:</para>
		<itemizedlist>
		<listitem>
		<para>probability (int, optional) - probability override</para>
		</listitem>
		</itemizedlist>
		<example>
			<title><function moreinfo="none">rand_event()</function> usage</title>
			<programlisting format="linespecific">
...
if (rand_event()) {
  append_to_reply("Retry-After: 120\n");
  sl_send_reply(503, "Try later");
  exit;
}
# normal message processing follows
...
</programlisting>
		</example>
	</section>

	<section id="func_rand_set_prob" xreflabel="rand_set_prob()">
	    <title><function moreinfo="none">rand_set_prob(probability)</function></title>
	    <para>
		Set the <quote>probability</quote> of the decision.
	    </para>
		<para>Parameters:</para>
		<itemizedlist>
		<listitem>
		<para>probability (int) - number ranging from 0 - 99, inclusively</para>
		</listitem>
		</itemizedlist>
	    <example>
		<title><function moreinfo="none">rand_set_prob()</function> usage</title>
		<programlisting format="linespecific">
...
rand_set_prob(4);
...
</programlisting>
	    </example>
	</section>

	<section id="func_rand_reset_prob" xreflabel="rand_reset_prob()">
	    <title><function moreinfo="none">rand_reset_prob()</function></title>
	    <para>
			Reset the probability back to the
			<xref linkend="param_initial_probability"/> value.
	    </para>
	    <example>
		<title><function moreinfo="none">rand_reset_prob()</function> usage</title>
		<programlisting format="linespecific">
...
rand_reset_prob();
...
</programlisting>
	    </example>
	</section>

	<section id="func_rand_get_prob" xreflabel="rand_get_prob()">
	    <title><function moreinfo="none">rand_get_prob()</function></title>
	    <para>
		Return the current probability setting, e.g. for logging purposes.
	    </para>
	    <example>
		<title><function moreinfo="none">rand_get_prob()</function> usage</title>
		<programlisting format="linespecific">
...
rand_get_prob();
   
</programlisting>
	    </example>
	</section>
	<section id="func_sleep" xreflabel="sleep()">
		<title>
			<function moreinfo="none">sleep(time)</function>
		</title>
		<para>
			Waits "time" seconds.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
			<listitem>
			<para><emphasis>time (int)</emphasis> - time to wait in seconds
			</para>
			</listitem>
		</itemizedlist>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>sleep</function> usage</title>
			<programlisting format="linespecific">
...
sleep(1);
...
$var(secs) = 10;
sleep($var(secs));
...
			</programlisting>
		</example>
	</section>
	
	<section id="func_usleep" xreflabel="usleep()">
		<title>
			<function moreinfo="none">usleep(time)</function>
		</title>
		<para>
			Waits "time" micro-seconds.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
			<listitem>
				<para><emphasis>time (int)</emphasis> - time to wait in micro-seconds
				</para>
			</listitem>
		</itemizedlist>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>usleep</function> usage</title>
			<programlisting format="linespecific">
...
usleep(500000); # sleep half a sec
...
			</programlisting>
		</example>
	</section>
	
	<section id="func_abort" xreflabel="abort()">
		<title>
			<function moreinfo="none">abort()</function>
		</title>
		<para>
			Debugging function that aborts the server. Depending on the
			configuration of the server a core dump will be created.
		</para>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>abort</function> usage</title>
			<programlisting format="linespecific">
...
abort();
...
			</programlisting>
		</example>
	</section>
	
	<section id="func_pkg_status" xreflabel="pkg_status()">
		<title>
			<function moreinfo="none">pkg_status()</function>
		</title>
		<para>
			Debugging function that dumps the status for the private (PKG) memory.
			This information is logged to the default log facility, depending on
			the general log level and the memlog setting. You need to compile
			the server with activated memory debugging to get detailed informations.
		</para>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>pkg_status</function> usage</title>
			<programlisting format="linespecific">
...
pkg_status();
...
			</programlisting>
		</example>
	</section>
	
	<section id="func_shm_status" xreflabel="shm_status()">
		<title>
			<function moreinfo="none">shm_status()</function>
		</title>
		<para>
			Debugging function that dumps the status for the shared (SHM) memory.
			This information is logged to the default log facility, depending on
			the general log level and the memlog setting. You need to compile
			the server with activated memory debugging to get detailed informations.
		</para>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>shm_status</function> usage</title>
			<programlisting format="linespecific">
...
shm_status();
...
			</programlisting>
		</example>
	</section>
	<section id="func_set_count" xreflabel="set_count()">
		<title>
			<function moreinfo="none">set_count(var_to_count, ret_var)</function>
		</title>
		<para>
			Counts the number of values of a given variable.
			It makes sense to call this function only for variables that can
			take more values (AVPs, headers).
		</para>
		<para>
			The result is returned in the second parameter.
		</para>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>set_count</function> usage</title>
			<programlisting format="linespecific">
...
set_count($avp(dids), $var(num_dids));
...
			</programlisting>
		</example>
	</section>
	<section id="func_set_select_weight" xreflabel="set_select_weight()">
		<title>
			<function moreinfo="none">set_select_weight(int_list_var)</function>
		</title>
		<para>
			This function selects an element from a set formed by the integer
			values of the given "int_list_var" variable. It applies the genetic
			algorithm - roulette-wheel selection to choose an element from a set.
			The probability of selecting a certain element is proportionate with
			its weight. It will return the index of that selected element.
		</para>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>set_select_weight</function> usage</title>
			<programlisting format="linespecific">
...
$var(next_gw_idx) = set_select_weight($avp(gw_success_rates));
...
			</programlisting>
		</example>
	</section>
	<section id="func_ts_usec_delta" xreflabel="ts_usec_delta()">
		<title>
			<function moreinfo="none">ts_usec_delta(t1_sec, t1_usec, t2_sec, t2_usec, [delta_str], [delta_int])</function>
		</title>
		<para>
			This function returns the absolute difference between the two given
			timestamps. The result is expressed as <emphasis>microseconds</emphasis>
			and can be returned as either string or integer.
		</para>
		<para>
			<emphasis role='bold'>WARNING:</emphasis> when using
			<emphasis>delta_int</emphasis>, the function will return error code
			<emphasis role='bold'>-1</emphasis> in case the difference overflows
			the signed integer holder! (i.e. a diff of ~35 minutes or more)
		</para>
		<para>
			This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
			FAILURE_ROUTE, BRANCH_ROUTE.
		</para>
		<example>
			<title><function>ts_usec_delta</function> usage</title>
			<programlisting format="linespecific">
...
ts_usec_delta($var(t1s), 300, 10, $var(t2us), $var(diff_str));
...
			</programlisting>
		</example>
	</section>
	<section id="func_check_time_rec" xreflabel="check_time_rec()">
		<title>
			<function moreinfo="none">check_time_rec(time_string, [timestamp])</function>
		</title>
		<para>
		The function returns a positive value if the specified time recurrence string
		matches the current time, or a negative value otherwise.
		</para>
		<para>
		For checking some other Unix timestamp than the current one, the second
		parameter will contain the intended timestamp to check.
		</para>
		<para>
		The syntax of each field is identical to the corresponding field from
		RFC 2445.
		</para>
		<para>
			This function may be used from any route.  It returns 1 on success
			and -1, -2 or -3 on failure, parsing or internal errors, respectively.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>time_string (string)</emphasis> - Time recurrence string which
			will be matched against the current time. Its fields are separated by "|" and
			the order in which they are given is: "timezone | dtstart | dtend | duration | freq
			| until | interval | byday | bymday | byyday | byweekno | bymonth".
			</para><para>None of the fields following "freq" is used unless
			"freq" is defined.  If the string ends in multiple null fields,
			they can all be ommited.
			</para><para>
			The "timezone" field is optional.  It represents the timezone in
			which to interpret the time recurrence elements (e.g. dtstart,
			dtend, until).  By default, the system time zone is used.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>timestamp (string, optional)</emphasis> - A
			specific Unix time to check.  The function simply expects the
			actual Unix time here, there is no need to perform any timezone
			adjustments.
			</para>
		</listitem>
		</itemizedlist>
		<para>
		Additionally, more complex time recurrence strings may be built by
		connecting multiple time recurrence strings (described above) using
		the logical AND ("&amp;"), OR ("/") and NEG ("!") operators.
		Furthermore, the expressions may be paranthesized.  Some examples:
		</para>
		<itemizedlist>
		<listitem>
			<para>
				20210104T080000|20211231T180000||WEEKLY|||MO,TU,WE,TH,FR
					&amp;
				!20210104T120000|20211231T140000||WEEKLY|||MO,TU,WE,TH,FR
			</para>
			<para>
				This example multi-recurrence expresses the working days schedule for
				company X during 2021:  workdays from 8-18, except the 12-14 interval,
				when everyone is out for lunch break and the business is closed.
				Since the timezone is omitted from each schedule, the operating
				system timezone will be used instead.
			</para>
		</listitem>
		<listitem>
			<para>
				America/New_York|20210104T090000|20210104T170000||WEEKLY|||MO,TU,WE,TH,FR
					&amp;
				!(Europe/Amsterdam|20210427T000000|20210428T000000 / Europe/London|20211227T000000|20211228T000000)
			</para>
			<para>
				This example multi-recurrence expresses the working days schedule for
				US-based company Y during 2021:  workdays from 9-17 (NY timezone),
				except european holidays such as King's Day (April 27th, NL) or
				the Spring Bank Holiday (May 31st, UK), when most of its
				workforce will have flown back to Europe.
			</para>
		</listitem>
		</itemizedlist>
		<example>
			<title><function>check_time_rec</function> usage</title>
			<programlisting format="linespecific">
...
# Only passing if still in 2012 and on a Bucharest-compatible timezone
if (check_time_rec("Europe/Bucharest|20120101T000000|20130101T000000"))
	xlog("Current system time matches the given Romanian time interval\n");
...
# Only passing if less than 30 days have passed from "dtstart", system timezone
if (check_time_rec("20121101T000000||p30d"))
	xlog("Current time matches the given interval\n");
...
			</programlisting>
		</example>
	</section>
	<section id="func_get_static_lock" xreflabel="get_static_lock()">
		<title>
		<function moreinfo="none">get_static_lock(key)</function>
		</title>
		<para>
		Acquire the static lock which corresponds to "key".  In case the
		lock is taken by another process, script execution will halt until the
		lock is released.  Attempting to acquire the lock a second time by the
		same process, without releasing it first, will result in a deadlock.
		</para>
		<para>
		The static lock functions guarantee that two different strings will never
		point to the same lock, thus avoiding introducing unnecessary
		(and transparent!) synchronization between processes. Their disadvantage is
		the nature of their parameters (static strings), making them inappropriate in
		certain scenarios.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>key (static string)</emphasis> - key to be hashed in
				order to obtain the index of a static lock
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, 
		BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE.
		</para>
		<example>
		<title><function moreinfo="none">get_static_lock</function> usage</title>
		<programlisting format="linespecific">
# acquire and release a static lock 
...
get_static_lock("Zone_1");
...
release_static_lock("Zone_1");
...
</programlisting>
		</example>
	</section>
	<section id="func_release_static_lock" xreflabel="release_static_lock()">
		<title>
		<function moreinfo="none">release_static_lock(key)</function>
		</title>
		<para>
		Release the static lock corresponding to "key". Nothing will happen if
		the lock is not acquired. 
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>key (static string)</emphasis> - key to be hashed in
				order to obtain the index of a static lock.
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, 
		BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE.
		</para>
		<example>
		<title><function moreinfo="none">release_static_lock</function> usage</title>
		<programlisting format="linespecific">
# acquire and release a static lock 
...
get_static_lock("Zone_1");
...
release_static_lock("Zone_1");
...
</programlisting>
		</example>
	</section>
	<section id="func_get_dynamic_lock" xreflabel="get_dynamic_lock()">
		<title>
		<function moreinfo="none">get_dynamic_lock(key)</function>
		</title>
		<para>
		Acquire the dynamic lock corresponding to "key".  In case the lock is
		taken by another process, script execution will halt until the lock is
		released.  Attempting to acquire the lock a second time by
		the same process, without releasing it first, will result in a deadlock.
		</para>
		<para>
		The dynamic lock functions have the advantage of allowing string
		variables to be given as parameters, but the drawback to this is that
		two strings may have the same hashed value, thus pointing to the same lock.
		As a consequence, either two totally separate regions of the script will be
		synchronized (they will not execute in parallel), or a process could end up
		in a deadlock by acquiring two locks in a row on two different (but equally
		hashed) strings. To address the latter issue, use the
		<xref linkend="func_strings_share_lock"/> function to test if two
		strings hash into the same dynamic lock.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>key (var)</emphasis> - key to be hashed in order to
			obtain the index of a dynamic lock from the pool
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, 
		BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE.
		</para>
		<example>
		<title><function moreinfo="none">get_dynamic_lock</function> usage</title>
		<programlisting format="linespecific">
...
# acquire and release a dynamic lock on the "Call-ID" header field value
if (!get_dynamic_lock($ci)) {
	xlog("Error while getting dynamic lock!\n");
}
...
if (!release_dynamic_lock($ci) {
	xlog("Error while releasing dynamic lock!\n");
}
...
</programlisting>
		</example>
	</section>
	<section id="func_release_dynamic_lock" xreflabel="release_dynamic_lock()">
		<title>
		<function moreinfo="none">release_dynamic_lock(key)</function>
		</title>
		<para>
		Release the dynamic lock corresponding to "key".  Nothing will happen
		if the lock is not acquired.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>key (var)</emphasis> - key to be hashed in order to
			obtain the index of a dynamic lock from the pool
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, 
		BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE.
		</para>
		<example>
		<title><function moreinfo="none">release_dynamic_lock</function> usage</title>
		<programlisting format="linespecific">
...
# acquire and release a dynamic lock on the "Call-ID" header field value
if (!get_dynamic_lock($ci)) {
	xlog("Error while getting dynamic lock!\n");
}
...
if (!release_dynamic_lock($ci) {
	xlog("Error while releasing dynamic lock!\n");
}
...
</programlisting>
		</example>
	</section>
	<section id="func_strings_share_lock" xreflabel="strings_share_lock()">
		<title>
		<function moreinfo="none">strings_share_lock(key1, key2)</function>
		</title>
		<para>
		A function used to test if two strings will generate the same hash value.
		Its purpose is to prevent deadlocks resulted when a process successively
		acquires two dynamic locks on two strings which happen to point to the same
		lock.
		</para>
		<para>
		Theoretically, the chance of two strings generating the same hash value 
		decreases proportionally to the increase of the
		<xref linkend="param_lock_pool_size"/> parameter. In
		other words, the more dynamic locks you configure the module with, the higher
		the chance that all individual protected regions of your script will run in
		parallel, without waiting for each other.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>key1, key2 (string)</emphasis> - strings which will have
			their hash values compared
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, 
		BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE|EVENT_ROUTE.
		</para>
		<example>
		<title><function moreinfo="none">strings_share_lock</function> usage</title>
		<programlisting format="linespecific">
...
# Proper way of acquiring two dynamic locks successively
if (!get_dynamic_lock($avp(foo))) {
	xlog("Error while getting dynamic lock!\n");
}

if (!strings_share_lock($avp(foo), $avp(bar)) {
	if (!get_dynamic_lock($avp(bar))) {
		xlog("Error while getting dynamic lock!\n");
	}
}
...
if (!strings_share_lock($avp(foo), $avp(bar)) {
	if (!release_dynamic_lock($avp(bar)) {
		xlog("Error while releasing dynamic lock!\n");
	}
}

if (!release_dynamic_lock($avp(foo)) {
	xlog("Error while releasing dynamic lock!\n");
}
...
</programlisting>
		</example>
	</section>

	<section id="func_get_accurate_time" xreflabel="get_accurate_time()">
		<title>
		<function moreinfo="none">get_accurate_time(sec, usec, [str_sec_usec])</function>
		</title>
		<para>
		Fetch the current Unix time epoch with microsecond precision.
		Optionally, print this value as a floating point number (3rd parameter).
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>sec (int)</emphasis> - the current Unix timestamp (integer part)
			</para>
		</listitem>
		<listitem>
			<para><emphasis>usec (int)</emphasis> - the current Unix timestamp (decimal part)
			</para>
		</listitem>
		<listitem>
			<para><emphasis>str_sec_usec (string, optional)</emphasis> - the current Unix
					timestamp as a floating point number (6-digit precision)
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE,
		BRANCH_ROUTE, LOCAL_ROUTE, STARTUP_ROUTE, TIMER_ROUTE, EVENT_ROUTE.
		</para>
		<example>
		<title><function moreinfo="none">get_accurate_time</function> usage</title>
		<programlisting format="linespecific">
...
get_accurate_time($var(sec), $var(usec));
xlog("Current Unix timestamp: $var(sec) s, $var(usec) us\n");
...
</programlisting>
		</example>
	</section>

		<section id="func_shuffle_avps" xreflabel="shuffle_avps()">
			<title>
				<function moreinfo="none">shuffle_avps(name)
				</function>
			</title>
			<para>
			Randomly shuffles AVPs with <emphasis>name</emphasis>.
			</para>
			<para>Meaning of the parameters is as follows:</para>
			<itemizedlist>
			<listitem>
				<para><emphasis>name (variable)</emphasis> - name of AVP to shuffle.
				</para>
			</listitem>
			</itemizedlist>
			<para>
			This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
			BRANCH_ROUTE, LOCAL_ROUTE and ONREPLY_ROUTE.
			</para>
			<example>
				<title><function>shuffle_avps</function> usage</title>
				<programlisting format="linespecific">
...
$avp(foo) := "str1";
$avp(foo)  = "str2";
$avp(foo)  = "str3";
xlog("Initial AVP list is: $(avp(foo)[*])\n");       # str3 str2 str1
if(shuffle_avps( $avp(foo) ))
    xlog("Shuffled AVP list is: $(avp(foo)[*])\n");  # str1, str3, str2 (for example)
...
				</programlisting>
			</example>
		</section>
	</section>

	<section>
	<title>Exported Asyncronous Functions</title>
	<section id="afunc_sleep" xreflabel="sleep()">
		<title>
		<function moreinfo="none">sleep(seconds)</function>
		</title>
		<para>
		Waits a number of seconds. This function does exactly the same as
		<xref linkend="func_sleep"/>,
		but in an asynchronous way. The script execution is suspended until
		the waiting is done; then OpenSIPS resumes the script execution via 
		the resume route.
		</para>
		<para>
		To read and understand more on the asynchronous functions, how to 
		use them and what are their advantages, please refer to the OpenSIPS
		online Manual.
		</para>
		<example>
		<title><function moreinfo="none">async sleep</function> usage</title>
		<programlisting format="linespecific">
{
...
async( sleep("5"), after_sleep );
}

route[after_sleep] {
...
}
</programlisting>
		</example>
	</section>

	<section id="afunc_usleep" xreflabel="usleep()">
		<title>
		<function moreinfo="none">usleep(seconds)</function>
		</title>
		<para>
		Waits a number of micro-seconds. This function does exactly the same as
		<xref linkend="func_usleep"/>,
		but in an asynchronous way. The script execution is suspended until
		the waiting is done; then OpenSIPS resumes the script execution via 
		the resume route.
		</para>
		<para>
		To read and understand more on the asynchronous functions, how to 
		use them and what are their advantages, please refer to the OpenSIPS
		online Manual.
		</para>
		<example>
		<title><function moreinfo="none">async usleep</function> usage</title>
		<programlisting format="linespecific">
{
...
async( usleep("1000"), after_usleep );
}

route[after_usleep] {
...
}
</programlisting>
		</example>
	</section>

	</section>

<section id="exported_mi_functions" xreflabel="Exported MI Functions">
		<title>Exported MI Functions</title>
		<section id="mi_rand_set_prop" xreflabel="rand_set_prop">
			<title><function moreinfo="none">rand_set_prop</function></title>
			<para>
				Set the probability value to the given parameter.
			</para>
			<para>Parameters:</para>
			<itemizedlist>
				<listitem><para>
					<emphasis>prob_proc</emphasis> - the parameter should be
					a percent value (number from 0 to 99).
				</para></listitem>
			</itemizedlist>
			<example>
				<title><function moreinfo="none">rand_set_prob</function> usage</title>
				<programlisting format="linespecific">
...
$ opensips-cli -x mi rand_set_prob 10
...
</programlisting>
			</example>
			
		</section>
		<section id="mi_rand_reset_prob" xreflabel="rand_reset_prob">
			<title><function moreinfo="none">rand_reset_prob</function></title>
			<para>
				Reset the probability value to the inital start value.
			</para>
			<para>
				This command don't need a parameter.
			</para>
			<example>
				<title>
					<function moreinfo="none">rand_reset_prob</function> usage</title>
				<programlisting format="linespecific">
...
$ opensips-cli -x mi rand_reset_prob
...
</programlisting>
			</example>
		</section>
		<section id="mi_rand_get_prob" xreflabel="rand_get_prob">
			<title><function moreinfo="none">rand_get_prob</function></title>
			<para>
				Return the actual probability setting.
			</para>
			<para>
				The function return the actual probability value.
			</para>
			<example>
				<title><function moreinfo="none">rand_get_prob</function> usage</title>
				<programlisting format="linespecific">
...
$ opensips-cli -x mi get_prob
The actual probability is 50 percent.
...
</programlisting>
			</example>
		</section>
		<section id="mi_check_config_hash" xreflabel="check_config_hash">
			<title><function moreinfo="none">check_config_hash</function></title>
			<para>
				Check if the actual config file hash is identical to the stored one.
			</para>
			<para>
				The function returns 200 OK if the hash values are identical, 400 if
				there are not identical, 404 if no file for hashing has been configured
				and 500 on errors. Additional a short text message is printed.
			</para>
			<example>
				<title><function moreinfo="none">check_config_hash</function> usage</title>
				<programlisting format="linespecific">
...
$ opensips-cli -x mi check_config_hash
The actual config file hash is identical to the stored one.
...
</programlisting>
			</example>
		</section>
		<section id="mi_get_config_hash" xreflabel="get_config_hash">
			<title><function moreinfo="none">get_config_hash</function></title>
			<para>
				Return the stored config file hash.
			</para>
			<para>
				The function returns 200 OK and the hash value on success or 404 if no
				file for hashing has been configured.
			</para>
			<example>
				<title><function moreinfo="none">get_config_hash</function> usage</title>
				<programlisting format="linespecific">
...
$ opensips-cli -x mi get_config_hash
1580a37104eb4de69ab9f31ce8d6e3e0
...
</programlisting>
			</example>
		</section>
		<section id="mi_shv_set" xreflabel="shv_set">
			<title><function moreinfo="none">shv_set</function></title>
			<para>
				Set the value of a shared variable ($shv(name)).
			</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para><emphasis>name</emphasis> : shared variable name</para></listitem>
			
			<listitem><para><emphasis>type</emphasis> : type of the value</para>
			      <itemizedlist>
	                 <listitem><para> <quote>int</quote>: integer value </para></listitem> 
			         <listitem><para> <quote>str</quote>: string value </para></listitem>	
				  </itemizedlist>
			</listitem>	  

			<listitem><para><emphasis>value</emphasis> : value to be set</para></listitem>
		</itemizedlist>
		<example>
		<title><function moreinfo="none">shv_set</function> usage</title>
		<programlisting format="linespecific">
...
$ opensips-cli -x mi shv_set debug int 0
...
</programlisting>
			</example>
		</section>
		<section id="mi_shv_get" xreflabel="shv_get">
			<title><function moreinfo="none">shv_get</function></title>
			<para>
				Get the value of a shared variable ($shv(name)).
			</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para><emphasis>name</emphasis> : shared variable name. If this parameter
			is missing, all shared variables are returned.</para></listitem>
		</itemizedlist>
		<example>
		<title><function moreinfo="none">shv_get</function> usage</title>
		<programlisting format="linespecific">
...
$ opensips-cli -x mi shv_get debug
$ opensips-cli -x mi shv_get
...
</programlisting>
			</example>
		</section>
</section>
	
<section id="exported_pseudo_variables">
	<title>Exported Pseudo-Variables</title>
		<section id="pv_env" xreflabel="$env">
			<title><varname>$env(name)</varname></title>
			<para>
				This PV provides access to the environment variable 'name'.
			</para>
			<example>
				<title><function moreinfo="none">env(name) pseudo-variable</function> usage</title>
				<programlisting format="linespecific">
...
xlog("PATH environment variable is $env(PATH)\n");
...
				 </programlisting>
			</example>
	</section>
	<section id="pv_RANDOM" xreflabel="$RANDOM">
			<title><varname>$RANDOM</varname></title>
			<para>
				Returns a random value from the [0 - 2^31) range.
			</para>
			<example>
				<title><function moreinfo="none">RANDOM pseudo-variable</function> usage</title>
				<programlisting format="linespecific">
...
$avp(10) = ($RANDOM / 16777216); # 2^24
if ($avp(10) &lt; 10) {
   $avp(10) = 10;
}
append_to_reply("Retry-After: $avp(10)\n");
sl_send_reply(503, "Try later");
exit;
# normal message processing follows
   
				 </programlisting>
			</example>
	</section>
	<section id="pv_ctime" xreflabel="$ctime">
			<title><varname>$ctime(name)</varname></title>
			<para>
			The PV provides access to broken-down time attributes.
			</para>
			<para>
			The <quote>name</quote> can be:
			</para>
			<itemizedlist>
				<listitem>
				<para><emphasis>sec</emphasis> - return seconds (int 0-59)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>min</emphasis> - return minutes (int 0-59)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>hour</emphasis> - return hours (int 0-23)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>mday</emphasis> - return the day of month (int 0-59)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>mon</emphasis> - return the month (int 1-12)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>year</emphasis> - return the year (int, e.g., 2008)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>wday</emphasis> - return the day of week (int, 1=Sunday - 7=Saturday)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>yday</emphasis> - return the day of year (int, 1-366)</para>
				</listitem>	  
				<listitem>
				<para><emphasis>isdst</emphasis> - return daylight saving time status (int, 0 - DST off, >0 DST on)</para>
				</listitem>	  
			</itemizedlist>
			<example>
				<title><function moreinfo="none">ctime(name) pseudo-variable</function> usage</title>
				<programlisting format="linespecific">
...
if ($ctime(year) == 2008) {
	xlog("request: $rm from $fu to $ru in year 2008\n");
}
...
				 </programlisting>
			</example>
	</section>
	<section id="pv_shv" xreflabel="$shv">
			<title><varname>$shv(name)</varname></title>
			<para>
				It is a class of pseudo-variables stored in shared memory. The
				value of $shv(name) is visible across all opensips processes.
				Each <quote>shv</quote> has single value and it is initialized
				to integer 0. You can use <quote>shvset</quote> parameter to
				initialize the shared variable. The module exports a set of MI
				functions to get/set the value of shared variables.
			</para>
			<example>
				<title><function moreinfo="none">shv(name) pseudo-variable</function> usage</title>
				<programlisting format="linespecific">
...
modparam("cfgutils", "shvset", "debug=i:1")
...
if ($shv(debug) == 1) {
	xlog("request: $rm from $fu to $ru\n");
}
...
				 </programlisting>
			</example>
	</section>
</section>
</chapter>

