Various code and documentation cleanups for res_config_sqlite

(closes issue #10711, rbraun_proformatique)


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@82321 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Russell Bryant
2007-09-13 15:26:40 +00:00
parent 5b1668603f
commit 1282de797d
4 changed files with 354 additions and 335 deletions

View File

@@ -9,7 +9,3 @@ dbfile => /var/lib/asterisk/sqlite.db
; support is simply disabled. ; support is simply disabled.
config_table => ast_config config_table => ast_config
cdr_table => ast_cdr cdr_table => ast_cdr
; This parameter controls the registration of the SQLITE() Dialplan application.
app_enable => yes

View File

@@ -1,5 +1,5 @@
/* /*
* res_sqlite - SQLite 2 support for Asterisk * res_config_sqlite - SQLite 2 support for Asterisk
* *
* This module can be used as a static/RealTime configuration module, and a CDR * This module can be used as a static/RealTime configuration module, and a CDR
* handler. See the Doxygen documentation for a detailed description of the * handler. See the Doxygen documentation for a detailed description of the
@@ -13,105 +13,109 @@
/* /*
* RealTime static table. * RealTime static table.
*/ */
CREATE TABLE ast_config CREATE TABLE ast_config (
( id INTEGER,
id INTEGER PRIMARY KEY, commented TINYINT(1) NOT NULL DEFAULT 0,
commented INT(11) NOT NULL DEFAULT '0', filename VARCHAR(128) NOT NULL DEFAULT '',
filename VARCHAR(128) NOT NULL, category VARCHAR(128) NOT NULL DEFAULT 'default',
category VARCHAR(128) NOT NULL, var_name VARCHAR(128) NOT NULL DEFAULT '',
var_name VARCHAR(128) NOT NULL, var_val TEXT NOT NULL DEFAULT '',
var_val VARCHAR(128) NOT NULL PRIMARY KEY (id)
); );
CREATE INDEX ast_config_filename_commented ON ast_config(filename, commented); CREATE INDEX ast_config__idx__filename_commented ON ast_config(filename, commented);
CREATE INDEX ast_config__idx__category ON ast_config(category);
/* /*
* CDR table (this table is automatically created if non existent). * CDR table (this table is automatically created if non existent).
*
* CREATE TABLE ast_cdr
* (
* id INTEGER PRIMARY KEY,
* clid VARCHAR(80) NOT NULL DEFAULT '',
* src VARCHAR(80) NOT NULL DEFAULT '',
* dst VARCHAR(80) NOT NULL DEFAULT '',
* dcontext VARCHAR(80) NOT NULL DEFAULT '',
* channel VARCHAR(80) NOT NULL DEFAULT '',
* dstchannel VARCHAR(80) NOT NULL DEFAULT '',
* lastapp VARCHAR(80) NOT NULL DEFAULT '',
* lastdata VARCHAR(80) NOT NULL DEFAULT '',
* start CHAR(19) NOT NULL DEFAULT '0000-00-00 00:00:00',
* answer CHAR(19) NOT NULL DEFAULT '0000-00-00 00:00:00',
* end CHAR(19) NOT NULL DEFAULT '0000-00-00 00:00:00',
* duration INT(11) NOT NULL DEFAULT '0',
* billsec INT(11) NOT NULL DEFAULT '0',
* disposition INT(11) NOT NULL DEFAULT '0',
* amaflags INT(11) NOT NULL DEFAULT '0',
* accountcode VARCHAR(20) NOT NULL DEFAULT '',
* uniqueid VARCHAR(32) NOT NULL DEFAULT '',
* userfield VARCHAR(255) NOT NULL DEFAULT ''
* );
*/ */
CREATE TABLE ast_cdr (
id INTEGER,
clid VARCHAR(80) NOT NULL DEFAULT '',
src VARCHAR(80) NOT NULL DEFAULT '',
dst VARCHAR(80) NOT NULL DEFAULT '',
dcontext VARCHAR(80) NOT NULL DEFAULT '',
channel VARCHAR(80) NOT NULL DEFAULT '',
dstchannel VARCHAR(80) NOT NULL DEFAULT '',
lastapp VARCHAR(80) NOT NULL DEFAULT '',
lastdata VARCHAR(80) NOT NULL DEFAULT '',
start DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
answer DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
end DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
duration INT(11) NOT NULL DEFAULT 0,
billsec INT(11) NOT NULL DEFAULT 0,
disposition VARCHAR(45) NOT NULL DEFAULT '',
amaflags INT(11) NOT NULL DEFAULT 0,
accountcode VARCHAR(20) NOT NULL DEFAULT '',
uniqueid VARCHAR(32) NOT NULL DEFAULT '',
userfield VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (id)
);
/* /*
* SIP RealTime table. * SIP RealTime table.
*/ */
CREATE TABLE ast_sip CREATE TABLE ast_sip (
( id INTEGER,
id INTEGER PRIMARY KEY, commented TINYINT(1) NOT NULL DEFAULT 0,
commented INT(11) NOT NULL DEFAULT '0', name VARCHAR(80) NOT NULL DEFAULT '',
name VARCHAR(80) NOT NULL, host VARCHAR(31) NOT NULL DEFAULT '',
accountcode VARCHAR(20), nat VARCHAR(5) NOT NULL DEFAULT 'no',
amaflags VARCHAR(13), type VARCHAR(6) NOT NULL DEFAULT 'friend',
callgroup VARCHAR(10), accountcode VARCHAR(20) DEFAULT NULL,
callerid VARCHAR(80), amaflags VARCHAR(13) DEFAULT NULL,
canreinvite CHAR(3), callgroup VARCHAR(10) DEFAULT NULL,
context VARCHAR(80), callerid VARCHAR(80) DEFAULT NULL,
defaultip VARCHAR(15), cancallforward CHAR(3) DEFAULT 'yes',
dtmfmode VARCHAR(7), canreinvite CHAR(3) DEFAULT 'yes',
fromuser VARCHAR(80), context VARCHAR(80) DEFAULT NULL,
fromdomain VARCHAR(80), defaultip VARCHAR(15) DEFAULT NULL,
fullcontact VARCHAR(80), dtmfmode VARCHAR(7) DEFAULT NULL,
host VARCHAR(31) NOT NULL, fromuser VARCHAR(80) DEFAULT NULL,
insecure VARCHAR(4), fromdomain VARCHAR(80) DEFAULT NULL,
language CHAR(2), insecure VARCHAR(4) DEFAULT NULL,
mailbox VARCHAR(50), language CHAR(2) DEFAULT NULL,
md5secret VARCHAR(80), mailbox VARCHAR(50) DEFAULT NULL,
nat VARCHAR(5) NOT NULL DEFAULT 'no', md5secret VARCHAR(80) DEFAULT NULL,
deny VARCHAR(95), deny VARCHAR(95) DEFAULT NULL,
permit VARCHAR(95), permit VARCHAR(95) DEFAULT NULL,
mask VARCHAR(95), mask VARCHAR(95) DEFAULT NULL,
pickupgroup VARCHAR(10), musiconhold VARCHAR(100) DEFAULT NULL,
port VARCHAR(5) NOT NULL, pickupgroup VARCHAR(10) DEFAULT NULL,
qualify CHAR(3), qualify CHAR(3) DEFAULT NULL,
restrictcid CHAR(1), regexten VARCHAR(80) DEFAULT NULL,
rtptimeout CHAR(3), restrictcid CHAR(3) DEFAULT NULL,
rtpholdtimeout CHAR(3), rtptimeout CHAR(3) DEFAULT NULL,
secret VARCHAR(80), rtpholdtimeout CHAR(3) DEFAULT NULL,
type VARCHAR(6) NOT NULL DEFAULT 'friend', secret VARCHAR(80) DEFAULT NULL,
username VARCHAR(80) NOT NULL, setvar VARCHAR(100) DEFAULT NULL,
disallow VARCHAR(100), disallow VARCHAR(100) DEFAULT 'all',
allow VARCHAR(100), allow VARCHAR(100) DEFAULT 'g729,ilbc,gsm,ulaw,alaw',
musiconhold VARCHAR(100), fullcontact VARCHAR(80) NOT NULL DEFAULT '',
regseconds INT(11) NOT NULL DEFAULT '0', ipaddr VARCHAR(15) NOT NULL DEFAULT '',
ipaddr VARCHAR(15) NOT NULL, port INT(11) NOT NULL DEFAULT 0,
regexten VARCHAR(80) NOT NULL, regserver VARCHAR(100) DEFAULT NULL,
cancallforward CHAR(3), regseconds INT(11) NOT NULL DEFAULT 0,
setvar VARCHAR(100) NOT NULL username VARCHAR(80) NOT NULL DEFAULT '',
PRIMARY KEY (id)
UNIQUE (name)
); );
CREATE UNIQUE INDEX ast_sip_name ON ast_sip(name); CREATE INDEX ast_sip__idx__commented ON ast_sip(commented);
/* /*
* Dialplan RealTime table. * Dialplan RealTime table.
*/ */
CREATE TABLE ast_exten CREATE TABLE ast_exten (
( id INTEGER,
id INTEGER PRIMARY KEY, commented TINYINT(1) NOT NULL DEFAULT 0,
commented INT(11) NOT NULL DEFAULT '0', context VARCHAR(80) NOT NULL DEFAULT '',
context VARCHAR(20) NOT NULL, exten VARCHAR(40) NOT NULL DEFAULT '',
exten VARCHAR(20) NOT NULL, priority INT(11) NOT NULL DEFAULT 0,
priority TINYINT(4) NOT NULL, app VARCHAR(128) NOT NULL DEFAULT '',
app VARCHAR(20) NOT NULL, appdata VARCHAR(128) NOT NULL DEFAULT '',
appdata VARCHAR(128) NOT NULL PRIMARY KEY (id)
); );
CREATE INDEX ast_exten__idx__commented ON ast_exten(commented);
CREATE INDEX ast_exten__idx__context_exten_priority ON ast_exten(context, exten, priority);

View File

@@ -252,6 +252,7 @@ DUNDi is not itself a Voice-over IP signaling or media protocol. Instead, it pub
* \arg \link Config_enum ENUM configuration \endlink * \arg \link Config_enum ENUM configuration \endlink
* \arg \link Config_moh Music on Hold configuration \endlink * \arg \link Config_moh Music on Hold configuration \endlink
* \arg \link Config_vm Voicemail configuration \endlink * \arg \link Config_vm Voicemail configuration \endlink
* \arg \link res_config_sqlite SQLite Resource driver configuration \endlink
*/ */
/*! \page Config_ast Asterisk.conf /*! \page Config_ast Asterisk.conf
@@ -551,3 +552,13 @@ DUNDi is not itself a Voice-over IP signaling or media protocol. Instead, it pub
* \arg \link Config_ami Configuration file \endlink * \arg \link Config_ami Configuration file \endlink
* \verbinclude ajam.txt * \verbinclude ajam.txt
*/ */
/*! \page res_config_sqlite SQLite Resource driver configuration
* \arg Implemented in \ref res_config_sqlite.c
* \arg Configuration file:
* \verbinclude res_config_sqlite.conf
* \arg SQL tables:
* \verbinclude res_config_sqlite.txt
* \arg See also:
* http://www.sqlite.org
*/

View File

@@ -30,27 +30,27 @@
* RealTime Architecture - ARA). * RealTime Architecture - ARA).
* It can also be used to log CDR entries. Finally, it can be used for simple * It can also be used to log CDR entries. Finally, it can be used for simple
* queries in the Dialplan. Note that Asterisk already comes with a module * queries in the Dialplan. Note that Asterisk already comes with a module
* named cdr_sqlite. There are two reasons for including it in res_sqlite: * named cdr_sqlite. There are two reasons for including it in res_config_sqlite:
* the first is that rewriting it was a training to learn how to write a * the first is that rewriting it was a training to learn how to write a
* simple module for Asterisk, the other is to have the same database open for * simple module for Asterisk, the other is to have the same database open for
* all kinds of operations, which improves reliability and performance. * all kinds of operations, which improves reliability and performance.
* *
* There is already a module for SQLite 3 (named res_sqlite3) in the Asterisk * There is already a module for SQLite 3 (named res_sqlite3) in the Asterisk
* addons. res_sqlite was developed because we, at Proformatique, are using * addons. res_config_sqlite was developed because we, at Proformatique, are using
* PHP 4 in our embedded systems, and PHP 4 has no stable support for SQLite 3 * PHP 4 in our embedded systems, and PHP 4 has no stable support for SQLite 3
* at this time. We also needed RealTime support. * at this time. We also needed RealTime support.
* *
* \section conf_sec Configuration * \section conf_sec Configuration
* *
* The main configuration file is res_config_sqlite.conf. It must be readable or * The main configuration file is res_config_sqlite.conf. It must be readable or
* res_sqlite will fail to start. It is suggested to use the sample file * res_config_sqlite will fail to start. It is suggested to use the sample file
* in this package as a starting point. The file has only one section * in this package as a starting point. The file has only one section
* named <code>general</code>. Here are the supported parameters : * named <code>general</code>. Here are the supported parameters :
* *
* <dl> * <dl>
* <dt><code>dbfile</code></dt> * <dt><code>dbfile</code></dt>
* <dd>The absolute path to the SQLite database (the file can be non existent, * <dd>The absolute path to the SQLite database (the file can be non existent,
* res_sqlite will create it if is has the appropriate rights)</dd> * res_config_sqlite will create it if it has the appropriate rights)</dd>
* <dt><code>config_table</code></dt> * <dt><code>config_table</code></dt>
* <dd>The table used for static configuration</dd> * <dd>The table used for static configuration</dd>
* <dt><code>cdr_table</code></dt> * <dt><code>cdr_table</code></dt>
@@ -58,29 +58,14 @@
* disabled)</dd> * disabled)</dd>
* </dl> * </dl>
* *
* To use res_sqlite for static and/or RealTime configuration, refer to the * To use res_config_sqlite for static and/or RealTime configuration, refer to the
* Asterisk documentation. The file tables.sql can be used to create the * Asterisk documentation. The file tables.sql can be used to create the
* needed tables. * needed tables.
* *
* The SQLITE() application is very similar to the MYSQL() application. You
* can find more details at
* <a href="http://voip-info.org/wiki/view/Asterisk+cmd+MYSQL">http://voip-info.org/wiki/view/Asterisk+cmd+MYSQL</a>.
* The main difference is that you cannot choose your database - it's the
* file set in the <code>dbfile</code> parameter. As a result, there is no
* Connect or Disconnect command, and there is no connid variable.
*
* \section status_sec Driver status * \section status_sec Driver status
* *
* The CLI command <code>show sqlite status</code> returns status information * The CLI command <code>show sqlite status</code> returns status information
* about the running driver. One information is more important than others: * about the running driver.
* the number of registered virtual machines. A SQLite virtual machine is
* created each time a SQLITE() query command is used. If the number of
* registered virtual machines isn't 0 (or near 0, since one or more SQLITE()
* commands can be running when requesting the module status) and increases
* over time, this probably means that you're badly using the application
* and you're creating resource leaks. You should check your Dialplan and
* reload res_sqlite (by unloading and then loading again - reloading isn't
* supported)
* *
* \section credits_sec Credits * \section credits_sec Credits
* *
@@ -89,7 +74,7 @@
/*! /*!
* \file * \file
* \brief res_sqlite module. * \brief res_config_sqlite module.
*/ */
/*** MODULEINFO /*** MODULEINFO
@@ -114,90 +99,81 @@
#include "asterisk/options.h" #include "asterisk/options.h"
#include "asterisk/linkedlists.h" #include "asterisk/linkedlists.h"
#define RES_SQLITE_NAME "res_sqlite" #define MACRO_BEGIN do {
#define RES_SQLITE_DRIVER "sqlite" #define MACRO_END } while (0)
#define RES_SQLITE_APP_DRIVER "SQLITE"
#define RES_SQLITE_DESCRIPTION "Resource Module for SQLite 2" #define RES_CONFIG_SQLITE_NAME "res_config_sqlite"
#define RES_SQLITE_CONF_FILE "res_config_sqlite.conf" #define RES_CONFIG_SQLITE_DRIVER "sqlite"
#define RES_SQLITE_APP_SYNOPSIS "Dialplan access to SQLite 2" #define RES_CONFIG_SQLITE_DESCRIPTION "Resource Module for SQLite 2"
#define RES_SQLITE_APP_DESCRIPTION \ #define RES_CONFIG_SQLITE_CONF_FILE "res_config_sqlite.conf"
"SQLITE(): " RES_SQLITE_APP_SYNOPSIS "\n" #define RES_CONFIG_SQLITE_STATUS_SUMMARY "Show status information about the SQLite 2 driver"
#define RES_SQLITE_STATUS_SUMMARY \ #define RES_CONFIG_SQLITE_STATUS_USAGE \
"Show status information about the SQLite 2 driver" "Usage: show sqlite status\n" \
#define RES_SQLITE_STATUS_USAGE \ " " RES_CONFIG_SQLITE_STATUS_SUMMARY "\n"
"Usage: show sqlite status\n" \
" " RES_SQLITE_STATUS_SUMMARY "\n"
enum { enum {
RES_SQLITE_CONFIG_ID, RES_CONFIG_SQLITE_CONFIG_ID,
RES_SQLITE_CONFIG_COMMENTED, RES_CONFIG_SQLITE_CONFIG_COMMENTED,
RES_SQLITE_CONFIG_FILENAME, RES_CONFIG_SQLITE_CONFIG_FILENAME,
RES_SQLITE_CONFIG_CATEGORY, RES_CONFIG_SQLITE_CONFIG_CATEGORY,
RES_SQLITE_CONFIG_VAR_NAME, RES_CONFIG_SQLITE_CONFIG_VAR_NAME,
RES_SQLITE_CONFIG_VAR_VAL, RES_CONFIG_SQLITE_CONFIG_VAR_VAL,
RES_SQLITE_CONFIG_COLUMNS, RES_CONFIG_SQLITE_CONFIG_COLUMNS,
}; };
/*! #define SET_VAR(config, to, from) \
* Limit the number of maximum simultaneous registered SQLite VMs to avoid MACRO_BEGIN \
* a denial of service attack. int __error; \
*/ \
#define RES_SQLITE_VM_MAX 1024 __error = set_var(&to, #to, from->value); \
\
#define SET_VAR(config, to, from) \ if (__error) { \
do \ ast_config_destroy(config); \
{ \ unload_config(); \
int __error; \ return 1; \
__error = set_var(&to, #to, from->value); \ } \
if (__error) \ MACRO_END
{ \
ast_config_destroy(config); \
unload_config(); \
return 1; \
} \
} \
while (0)
/*! /*!
* Maximum number of loops before giving up executing a query. Calls to * Maximum number of loops before giving up executing a query. Calls to
* sqlite_xxx() functions which can return SQLITE_BUSY or SQLITE_LOCKED * sqlite_xxx() functions which can return SQLITE_BUSY or SQLITE_LOCKED
* are enclosed by RES_SQLITE_BEGIN and RES_SQLITE_END, e.g. * are enclosed by RES_CONFIG_SQLITE_BEGIN and RES_CONFIG_SQLITE_END, e.g.
* <pre> * <pre>
* char *errormsg; * char *errormsg;
* int error; * int error;
* *
* RES_SQLITE_BEGIN * RES_CONFIG_SQLITE_BEGIN
* error = sqlite_exec(db, query, NULL, NULL, &errormsg); * error = sqlite_exec(db, query, NULL, NULL, &errormsg);
* RES_SQLITE_END(error) * RES_CONFIG_SQLITE_END(error)
* *
* if (error) * if (error)
* ...; * ...;
* </pre> * </pre>
*/ */
#define RES_SQLITE_MAX_LOOPS 10 #define RES_CONFIG_SQLITE_MAX_LOOPS 10
/*! /*!
* Macro used before executing a query. * Macro used before executing a query.
* *
* \see RES_SQLITE_MAX_LOOPS. * \see RES_CONFIG_SQLITE_MAX_LOOPS.
*/ */
#define RES_SQLITE_BEGIN \ #define RES_CONFIG_SQLITE_BEGIN \
{ \ MACRO_BEGIN \
int __i; \ int __i; \
for (__i = 0; __i < RES_SQLITE_MAX_LOOPS; __i++) \ \
{ for (__i = 0; __i < RES_CONFIG_SQLITE_MAX_LOOPS; __i++) {
/*! /*!
* Macro used after executing a query. * Macro used after executing a query.
* *
* \see RES_SQLITE_MAX_LOOPS. * \see RES_CONFIG_SQLITE_MAX_LOOPS.
*/ */
#define RES_SQLITE_END(error) \ #define RES_CONFIG_SQLITE_END(error) \
if (error != SQLITE_BUSY && error != SQLITE_LOCKED) \ if (error != SQLITE_BUSY && error != SQLITE_LOCKED) \
break; \ break; \
usleep(1000); \ usleep(1000); \
} \ } \
} MACRO_END;
/*! /*!
* Structure sent to the SQLite callback function for static configuration. * Structure sent to the SQLite callback function for static configuration.
@@ -276,7 +252,7 @@ static int cdr_handler(struct ast_cdr *cdr);
* *
* This function is passed to the SQLite engine as a callback function to * This function is passed to the SQLite engine as a callback function to
* parse a row and store it in a struct ast_config object. It relies on * parse a row and store it in a struct ast_config object. It relies on
* resulting rows being sorted by category. * resulting rows being sorted by category.
* *
* \param arg a pointer to a struct cfg_entry_args object * \param arg a pointer to a struct cfg_entry_args object
* \param argc number of columns * \param argc number of columns
@@ -307,7 +283,7 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames);
* \see add_cfg_entry() * \see add_cfg_entry()
*/ */
static struct ast_config * config_handler(const char *database, const char *table, const char *file, static struct ast_config * config_handler(const char *database, const char *table, const char *file,
struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl); struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl);
/*! /*!
* \brief Helper function to parse a va_list object into 2 dynamic arrays of * \brief Helper function to parse a va_list object into 2 dynamic arrays of
@@ -362,8 +338,8 @@ static int add_rt_cfg_entry(void *arg, int argc, char **argv,
* table WHERE name = '123';</code>. * table WHERE name = '123';</code>.
* *
* \param database the database to use (ignored) * \param database the database to use (ignored)
* \param table the table to use * \param table the table to use
* \param ap list of parameters and values to match * \param ap list of parameters and values to match
* *
* \retval a linked list of struct ast_variable objects * \retval a linked list of struct ast_variable objects
* \retval NULL if an error occurred * \retval NULL if an error occurred
@@ -377,7 +353,7 @@ static struct ast_variable * realtime_handler(const char *database,
* *
* This function performs the same actions as add_rt_cfg_entry() except * This function performs the same actions as add_rt_cfg_entry() except
* that the rt_multi_cfg_entry_args structure is designed to store * that the rt_multi_cfg_entry_args structure is designed to store
* categories in addition of variables. * categories in addition to variables.
* *
* \param arg a pointer to a struct rt_multi_cfg_entry_args object * \param arg a pointer to a struct rt_multi_cfg_entry_args object
* \param argc number of columns * \param argc number of columns
@@ -407,8 +383,7 @@ static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv,
* \see add_rt_multi_cfg_entry() * \see add_rt_multi_cfg_entry()
*/ */
static struct ast_config * realtime_multi_handler(const char *database, static struct ast_config * realtime_multi_handler(const char *database,
const char *table, const char *table, va_list ap);
va_list ap);
/*! /*!
* \brief Asterisk callback function for RealTime configuration (variable * \brief Asterisk callback function for RealTime configuration (variable
@@ -429,8 +404,7 @@ static struct ast_config * realtime_multi_handler(const char *database,
* \retval -1 if an error occurred. * \retval -1 if an error occurred.
*/ */
static int realtime_update_handler(const char *database, const char *table, static int realtime_update_handler(const char *database, const char *table,
const char *keyfield, const char *entity, const char *keyfield, const char *entity, va_list ap);
va_list ap);
/*! /*!
* \brief Asterisk callback function for RealTime configuration (variable * \brief Asterisk callback function for RealTime configuration (variable
@@ -469,8 +443,7 @@ static int realtime_store_handler(const char *database, const char *table,
* \retval -1 if an error occurred. * \retval -1 if an error occurred.
*/ */
static int realtime_destroy_handler(const char *database, const char *table, static int realtime_destroy_handler(const char *database, const char *table,
const char *keyfield, const char *entity, const char *keyfield, const char *entity, va_list ap);
va_list ap);
/*! /*!
* \brief Asterisk callback function for the CLI status command. * \brief Asterisk callback function for the CLI status command.
@@ -503,16 +476,13 @@ static char *config_table;
/*! The name of the table used to store CDR entries. */ /*! The name of the table used to store CDR entries. */
static char *cdr_table; static char *cdr_table;
/*! The number of registered virtual machines. */
static int vm_count;
/*! /*!
* The structure specifying all callback functions used by Asterisk for static * The structure specifying all callback functions used by Asterisk for static
* and RealTime configuration. * and RealTime configuration.
*/ */
static struct ast_config_engine sqlite_engine = static struct ast_config_engine sqlite_engine =
{ {
.name = RES_SQLITE_DRIVER, .name = RES_CONFIG_SQLITE_DRIVER,
.load_func = config_handler, .load_func = config_handler,
.realtime_func = realtime_handler, .realtime_func = realtime_handler,
.realtime_multi_func = realtime_multi_handler, .realtime_multi_func = realtime_multi_handler,
@@ -523,7 +493,6 @@ static struct ast_config_engine sqlite_engine =
/*! /*!
* The mutex used to prevent simultaneous access to the SQLite database. * The mutex used to prevent simultaneous access to the SQLite database.
* SQLite isn't always compiled with thread safety.
*/ */
AST_MUTEX_DEFINE_STATIC(mutex); AST_MUTEX_DEFINE_STATIC(mutex);
@@ -535,8 +504,8 @@ static struct ast_cli_entry cli_status_cmd =
{ {
.cmda = {"show", "sqlite", "status", NULL}, .cmda = {"show", "sqlite", "status", NULL},
.handler = cli_status, .handler = cli_status,
.summary = RES_SQLITE_STATUS_SUMMARY, .summary = RES_CONFIG_SQLITE_STATUS_SUMMARY,
.usage = RES_SQLITE_STATUS_USAGE .usage = RES_CONFIG_SQLITE_STATUS_USAGE
}; };
/* /*
@@ -545,32 +514,33 @@ static struct ast_cli_entry cli_status_cmd =
/*! SQL query format to create the CDR table if non existent. */ /*! SQL query format to create the CDR table if non existent. */
static char *sql_create_cdr_table = static char *sql_create_cdr_table =
"CREATE TABLE '%q' (" "CREATE TABLE '%q' (\n"
" id INTEGER PRIMARY KEY," " id INTEGER,\n"
" clid VARCHAR(80) NOT NULL DEFAULT ''," " clid VARCHAR(80) NOT NULL DEFAULT '',\n"
" src VARCHAR(80) NOT NULL DEFAULT ''," " src VARCHAR(80) NOT NULL DEFAULT '',\n"
" dst VARCHAR(80) NOT NULL DEFAULT ''," " dst VARCHAR(80) NOT NULL DEFAULT '',\n"
" dcontext VARCHAR(80) NOT NULL DEFAULT ''," " dcontext VARCHAR(80) NOT NULL DEFAULT '',\n"
" channel VARCHAR(80) NOT NULL DEFAULT ''," " channel VARCHAR(80) NOT NULL DEFAULT '',\n"
" dstchannel VARCHAR(80) NOT NULL DEFAULT ''," " dstchannel VARCHAR(80) NOT NULL DEFAULT '',\n"
" lastapp VARCHAR(80) NOT NULL DEFAULT ''," " lastapp VARCHAR(80) NOT NULL DEFAULT '',\n"
" lastdata VARCHAR(80) NOT NULL DEFAULT ''," " lastdata VARCHAR(80) NOT NULL DEFAULT '',\n"
" start CHAR(19) NOT NULL DEFAULT '0000-00-00 00:00:00'," " start DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
" answer CHAR(19) NOT NULL DEFAULT '0000-00-00 00:00:00'," " answer DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
" end CHAR(19) NOT NULL DEFAULT '0000-00-00 00:00:00'," " end DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
" duration INT(11) NOT NULL DEFAULT '0'," " duration INT(11) NOT NULL DEFAULT 0,\n"
" billsec INT(11) NOT NULL DEFAULT '0'," " billsec INT(11) NOT NULL DEFAULT 0,\n"
" disposition INT(11) NOT NULL DEFAULT '0'," " disposition VARCHAR(45) NOT NULL DEFAULT '',\n"
" amaflags INT(11) NOT NULL DEFAULT '0'," " amaflags INT(11) NOT NULL DEFAULT 0,\n"
" accountcode VARCHAR(20) NOT NULL DEFAULT ''," " accountcode VARCHAR(20) NOT NULL DEFAULT '',\n"
" uniqueid VARCHAR(32) NOT NULL DEFAULT ''," " uniqueid VARCHAR(32) NOT NULL DEFAULT '',\n"
" userfield VARCHAR(255) NOT NULL DEFAULT ''" " userfield VARCHAR(255) NOT NULL DEFAULT '',\n"
" PRIMARY KEY (id)\n"
");"; ");";
/*! SQL query format to insert a CDR entry. */ /*! SQL query format to insert a CDR entry. */
static char *sql_add_cdr_entry = static char *sql_add_cdr_entry =
"INSERT INTO '%q' (" "INSERT INTO '%q' ("
" clid," " clid,"
" src," " src,"
" dst," " dst,"
" dcontext," " dcontext,"
@@ -655,10 +625,10 @@ static int load_config(void)
int error; int error;
struct ast_flags config_flags = { 0 }; struct ast_flags config_flags = { 0 };
config = ast_config_load(RES_SQLITE_CONF_FILE, config_flags); config = ast_config_load(RES_CONFIG_SQLITE_CONF_FILE, config_flags);
if (!config) { if (!config) {
ast_log(LOG_ERROR, "Unable to load " RES_SQLITE_CONF_FILE "\n"); ast_log(LOG_ERROR, "Unable to load " RES_CONFIG_SQLITE_CONF_FILE "\n");
return 1; return 1;
} }
@@ -696,27 +666,37 @@ static void unload_config(void)
static int cdr_handler(struct ast_cdr *cdr) static int cdr_handler(struct ast_cdr *cdr)
{ {
char *errormsg; char *query, *errormsg;
int error; int error;
query = sqlite_mprintf(sql_add_cdr_entry, cdr_table, cdr->clid,
cdr->src, cdr->dst, cdr->dcontext, cdr->channel,
cdr->dstchannel, cdr->lastapp, cdr->lastdata,
cdr->start.tv_sec, cdr->answer.tv_sec,
cdr->end.tv_sec, cdr->duration, cdr->billsec,
cdr->disposition, cdr->amaflags, cdr->accountcode,
cdr->uniqueid, cdr->userfield);
if (!query) {
ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
return 1;
}
ast_debug(1, "SQL query: %s\n", query);
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec_printf(db, sql_add_cdr_entry, NULL, NULL, &errormsg, error = sqlite_exec(db, query, NULL, NULL, &errormsg);
cdr_table, cdr->clid, cdr->src, cdr->dst, RES_CONFIG_SQLITE_END(error)
cdr->dcontext, cdr->channel, cdr->dstchannel,
cdr->lastapp, cdr->lastdata, cdr->start.tv_sec,
cdr->answer.tv_sec, cdr->end.tv_sec,
cdr->duration, cdr->billsec, cdr->disposition,
cdr->amaflags, cdr->accountcode, cdr->uniqueid,
cdr->userfield);
RES_SQLITE_END(error)
ast_mutex_unlock(&mutex); ast_mutex_unlock(&mutex);
sqlite_freemem(query);
if (error) { if (error) {
ast_log(LOG_ERROR, "%s\n", errormsg); ast_log(LOG_ERROR, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
return 1; return 1;
} }
@@ -728,15 +708,15 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
struct cfg_entry_args *args; struct cfg_entry_args *args;
struct ast_variable *var; struct ast_variable *var;
if (argc != RES_SQLITE_CONFIG_COLUMNS) { if (argc != RES_CONFIG_SQLITE_CONFIG_COLUMNS) {
ast_log(LOG_WARNING, "Corrupt table\n"); ast_log(LOG_WARNING, "Corrupt table\n");
return 1; return 1;
} }
args = arg; args = arg;
if (!args->cat_name || strcmp(args->cat_name, argv[RES_SQLITE_CONFIG_CATEGORY])) { if (!args->cat_name || strcmp(args->cat_name, argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY])) {
args->cat = ast_category_new(argv[RES_SQLITE_CONFIG_CATEGORY], "", 99999); args->cat = ast_category_new(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY], "", 99999);
if (!args->cat) { if (!args->cat) {
ast_log(LOG_WARNING, "Unable to allocate category\n"); ast_log(LOG_WARNING, "Unable to allocate category\n");
@@ -744,7 +724,7 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
} }
ast_free(args->cat_name); ast_free(args->cat_name);
args->cat_name = ast_strdup(argv[RES_SQLITE_CONFIG_CATEGORY]); args->cat_name = ast_strdup(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY]);
if (!args->cat_name) { if (!args->cat_name) {
ast_category_destroy(args->cat); ast_category_destroy(args->cat);
@@ -754,8 +734,7 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
ast_category_append(args->cfg, args->cat); ast_category_append(args->cfg, args->cat);
} }
var = ast_variable_new(argv[RES_SQLITE_CONFIG_VAR_NAME], var = ast_variable_new(argv[RES_CONFIG_SQLITE_CONFIG_VAR_NAME], argv[RES_CONFIG_SQLITE_CONFIG_VAR_VAL], "");
argv[RES_SQLITE_CONFIG_VAR_VAL], "");
if (!var) { if (!var) {
ast_log(LOG_WARNING, "Unable to allocate variable"); ast_log(LOG_WARNING, "Unable to allocate variable");
@@ -768,10 +747,10 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
} }
static struct ast_config *config_handler(const char *database, const char *table, const char *file, static struct ast_config *config_handler(const char *database, const char *table, const char *file,
struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl) struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl)
{ {
struct cfg_entry_args args; struct cfg_entry_args args;
char *errormsg; char *query, *errormsg;
int error; int error;
if (!config_table) { if (!config_table) {
@@ -782,24 +761,32 @@ struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl)
} else } else
table = config_table; table = config_table;
query = sqlite_mprintf(sql_get_config_table, table, file);
if (!query) {
ast_log(LOG_WARNING, "Unable to allocate SQL query\n");
return NULL;
}
ast_debug(1, "SQL query: %s\n", query);
args.cfg = cfg; args.cfg = cfg;
args.cat = NULL; args.cat = NULL;
args.cat_name = NULL; args.cat_name = NULL;
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec_printf(db, sql_get_config_table, add_cfg_entry, error = sqlite_exec(db, query, add_cfg_entry, &args, &errormsg);
&args, &errormsg, table, file); RES_CONFIG_SQLITE_END(error)
RES_SQLITE_END(error)
ast_mutex_unlock(&mutex); ast_mutex_unlock(&mutex);
ast_free(args.cat_name); ast_free(args.cat_name);
sqlite_freemem(query);
if (error) { if (error) {
ast_log(LOG_ERROR, "%s\n", errormsg); ast_log(LOG_ERROR, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
return NULL; return NULL;
} }
@@ -873,8 +860,7 @@ static int add_rt_cfg_entry(void *arg, int argc, char **argv, char **columnNames
return 0; return 0;
} }
static struct ast_variable * static struct ast_variable * realtime_handler(const char *database, const char *table, va_list ap)
realtime_handler(const char *database, const char *table, va_list ap)
{ {
char *query, *errormsg, *op, *tmp_str; char *query, *errormsg, *op, *tmp_str;
struct rt_cfg_entry_args args; struct rt_cfg_entry_args args;
@@ -913,8 +899,7 @@ realtime_handler(const char *database, const char *table, va_list ap)
for (i = 1; i < params_count; i++) { for (i = 1; i < params_count; i++) {
op = (strchr(params[i], ' ') == NULL) ? " =" : ""; op = (strchr(params[i], ' ') == NULL) ? " =" : "";
tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, params[i], op, tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, params[i], op, vals[i]);
vals[i]);
sqlite_freemem(query); sqlite_freemem(query);
if (!tmp_str) { if (!tmp_str) {
@@ -946,9 +931,9 @@ realtime_handler(const char *database, const char *table, va_list ap)
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, query, add_rt_cfg_entry, &args, &errormsg); error = sqlite_exec(db, query, add_rt_cfg_entry, &args, &errormsg);
RES_SQLITE_END(error) RES_CONFIG_SQLITE_END(error)
ast_mutex_unlock(&mutex); ast_mutex_unlock(&mutex);
@@ -956,7 +941,7 @@ realtime_handler(const char *database, const char *table, va_list ap)
if (error) { if (error) {
ast_log(LOG_WARNING, "%s\n", errormsg); ast_log(LOG_WARNING, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
ast_variables_destroy(args.var); ast_variables_destroy(args.var);
return NULL; return NULL;
} }
@@ -972,7 +957,7 @@ static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, char **colum
char *cat_name; char *cat_name;
size_t i; size_t i;
args = (struct rt_multi_cfg_entry_args *)arg; args = arg;
cat_name = NULL; cat_name = NULL;
/* /*
@@ -1076,8 +1061,7 @@ static struct ast_config *realtime_multi_handler(const char *database,
for (i = 1; i < params_count; i++) { for (i = 1; i < params_count; i++) {
op = (!strchr(params[i], ' ')) ? " =" : ""; op = (!strchr(params[i], ' ')) ? " =" : "";
tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, params[i], op, tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, params[i], op, vals[i]);
vals[i]);
sqlite_freemem(query); sqlite_freemem(query);
if (!tmp_str) { if (!tmp_str) {
@@ -1111,9 +1095,9 @@ static struct ast_config *realtime_multi_handler(const char *database,
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, query, add_rt_multi_cfg_entry, &args, &errormsg); error = sqlite_exec(db, query, add_rt_multi_cfg_entry, &args, &errormsg);
RES_SQLITE_END(error) RES_CONFIG_SQLITE_END(error)
ast_mutex_unlock(&mutex); ast_mutex_unlock(&mutex);
@@ -1122,7 +1106,7 @@ static struct ast_config *realtime_multi_handler(const char *database,
if (error) { if (error) {
ast_log(LOG_WARNING, "%s\n", errormsg); ast_log(LOG_WARNING, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
ast_config_destroy(cfg); ast_config_destroy(cfg);
return NULL; return NULL;
} }
@@ -1131,8 +1115,7 @@ static struct ast_config *realtime_multi_handler(const char *database,
} }
static int realtime_update_handler(const char *database, const char *table, static int realtime_update_handler(const char *database, const char *table,
const char *keyfield, const char *entity, const char *keyfield, const char *entity, va_list ap)
va_list ap)
{ {
char *query, *errormsg, *tmp_str; char *query, *errormsg, *tmp_str;
const char **params, **vals; const char **params, **vals;
@@ -1163,8 +1146,7 @@ static int realtime_update_handler(const char *database, const char *table,
size_t i; size_t i;
for (i = 1; i < params_count; i++) { for (i = 1; i < params_count; i++) {
tmp_str = sqlite_mprintf("%s, %q = '%q'", query, params[i], tmp_str = sqlite_mprintf("%s, %q = '%q'", query, params[i], vals[i]);
vals[i]);
sqlite_freemem(query); sqlite_freemem(query);
if (!tmp_str) { if (!tmp_str) {
@@ -1192,9 +1174,9 @@ static int realtime_update_handler(const char *database, const char *table,
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, query, NULL, NULL, &errormsg); error = sqlite_exec(db, query, NULL, NULL, &errormsg);
RES_SQLITE_END(error) RES_CONFIG_SQLITE_END(error)
if (!error) if (!error)
rows_num = sqlite_changes(db); rows_num = sqlite_changes(db);
@@ -1207,7 +1189,7 @@ static int realtime_update_handler(const char *database, const char *table,
if (error) { if (error) {
ast_log(LOG_WARNING, "%s\n", errormsg); ast_log(LOG_WARNING, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
} }
return rows_num; return rows_num;
@@ -1282,9 +1264,9 @@ static int realtime_store_handler(const char *database, const char *table, va_li
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, tmp_str, NULL, NULL, &errormsg); error = sqlite_exec(db, tmp_str, NULL, NULL, &errormsg);
RES_SQLITE_END(error) RES_CONFIG_SQLITE_END(error)
if (!error) { if (!error) {
rows_id = sqlite_last_insert_rowid(db); rows_id = sqlite_last_insert_rowid(db);
@@ -1298,15 +1280,14 @@ static int realtime_store_handler(const char *database, const char *table, va_li
if (error) { if (error) {
ast_log(LOG_WARNING, "%s\n", errormsg); ast_log(LOG_WARNING, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
} }
return rows_id; return rows_id;
} }
static int realtime_destroy_handler(const char *database, const char *table, static int realtime_destroy_handler(const char *database, const char *table,
const char *keyfield, const char *entity, const char *keyfield, const char *entity, va_list ap)
va_list ap)
{ {
char *query, *errormsg, *tmp_str; char *query, *errormsg, *tmp_str;
const char **params, **vals; const char **params, **vals;
@@ -1356,14 +1337,13 @@ static int realtime_destroy_handler(const char *database, const char *table,
} }
sqlite_freemem(query); sqlite_freemem(query);
query = tmp_str; query = tmp_str;
ast_debug(1, "SQL query: %s\n", query); ast_debug(1, "SQL query: %s\n", query);
ast_mutex_lock(&mutex); ast_mutex_lock(&mutex);
RES_SQLITE_BEGIN RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, query, NULL, NULL, &errormsg); error = sqlite_exec(db, query, NULL, NULL, &errormsg);
RES_SQLITE_END(error) RES_CONFIG_SQLITE_END(error)
if (!error) if (!error)
rows_num = sqlite_changes(db); rows_num = sqlite_changes(db);
@@ -1376,7 +1356,7 @@ static int realtime_destroy_handler(const char *database, const char *table,
if (error) { if (error) {
ast_log(LOG_WARNING, "%s\n", errormsg); ast_log(LOG_WARNING, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
} }
return rows_num; return rows_num;
@@ -1409,7 +1389,7 @@ static int unload_module(void)
ast_cli_unregister(&cli_status_cmd); ast_cli_unregister(&cli_status_cmd);
if (cdr_registered) if (cdr_registered)
ast_cdr_unregister(RES_SQLITE_NAME); ast_cdr_unregister(RES_CONFIG_SQLITE_NAME);
ast_config_engine_deregister(&sqlite_engine); ast_config_engine_deregister(&sqlite_engine);
@@ -1432,7 +1412,6 @@ static int load_module(void)
dbfile = NULL; dbfile = NULL;
config_table = NULL; config_table = NULL;
cdr_table = NULL; cdr_table = NULL;
vm_count = 0;
error = load_config(); error = load_config();
if (error) if (error)
@@ -1440,7 +1419,7 @@ static int load_module(void)
if (!(db = sqlite_open(dbfile, 0660, &errormsg))) { if (!(db = sqlite_open(dbfile, 0660, &errormsg))) {
ast_log(LOG_ERROR, "%s\n", errormsg); ast_log(LOG_ERROR, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
unload_module(); unload_module();
return 1; return 1;
} }
@@ -1448,10 +1427,28 @@ static int load_module(void)
ast_config_engine_register(&sqlite_engine); ast_config_engine_register(&sqlite_engine);
if (use_cdr) { if (use_cdr) {
RES_SQLITE_BEGIN char *query;
error = sqlite_exec_printf(db, "SELECT COUNT(id) FROM %Q;", NULL, NULL,
&errormsg, cdr_table); /* \cond DOXYGEN_CAN_PARSE_THIS */
RES_SQLITE_END(error) #undef QUERY
#define QUERY "SELECT COUNT(id) FROM %Q;"
/* \endcond */
query = sqlite_mprintf(QUERY, cdr_table);
if (!query) {
ast_log(LOG_ERROR, "Unable to allocate SQL query\n");
unload_module();
return 1;
}
ast_debug(1, "SQL query: %s\n", query);
RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, query, NULL, NULL, &errormsg);
RES_CONFIG_SQLITE_END(error)
sqlite_freemem(query);
if (error) { if (error) {
/* /*
@@ -1459,26 +1456,37 @@ static int load_module(void)
*/ */
if (error != SQLITE_ERROR) { if (error != SQLITE_ERROR) {
ast_log(LOG_ERROR, "%s\n", errormsg); ast_log(LOG_ERROR, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
unload_module(); unload_module();
return 1; return 1;
} }
RES_SQLITE_BEGIN sqlite_freemem(errormsg);
error = sqlite_exec_printf(db, sql_create_cdr_table, NULL, NULL, query = sqlite_mprintf(sql_create_cdr_table, cdr_table);
&errormsg, cdr_table);
RES_SQLITE_END(error) if (!query) {
ast_log(LOG_ERROR, "Unable to allocate SQL query\n");
unload_module();
return 1;
}
ast_debug(1, "SQL query: %s\n", query);
RES_CONFIG_SQLITE_BEGIN
error = sqlite_exec(db, query, NULL, NULL, &errormsg);
RES_CONFIG_SQLITE_END(error)
sqlite_freemem(query);
if (error) { if (error) {
ast_log(LOG_ERROR, "%s\n", errormsg); ast_log(LOG_ERROR, "%s\n", errormsg);
ast_free(errormsg); sqlite_freemem(errormsg);
unload_module(); unload_module();
return 1; return 1;
} }
} }
error = ast_cdr_register(RES_SQLITE_NAME, RES_SQLITE_DESCRIPTION, error = ast_cdr_register(RES_CONFIG_SQLITE_NAME, RES_CONFIG_SQLITE_DESCRIPTION, cdr_handler);
cdr_handler);
if (error) { if (error) {
unload_module(); unload_module();