From: Joshua C. Colp Date: Mon, 16 Aug 2021 18:26:12 +0000 (-0300) Subject: res_config_sqlite: Remove deprecated module. X-Git-Tag: 20.0.0-rc1~409 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=800fd84af633f544b62d4aa4872ea20d53f7c722;p=thirdparty%2Fasterisk.git res_config_sqlite: Remove deprecated module. ASTERISK-29598 Change-Id: I8ef17023f55bf01f2e309b06f4778a8ca7252c91 --- diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in index 89c9aa2dbd..37c3be6e78 100644 --- a/build_tools/menuselect-deps.in +++ b/build_tools/menuselect-deps.in @@ -58,7 +58,6 @@ SPEEX=@PBX_SPEEX@ SPEEXDSP=@PBX_SPEEXDSP@ SPEEX_PREPROCESS=@PBX_SPEEX_PREPROCESS@ SQLITE3=@PBX_SQLITE3@ -SQLITE=@PBX_SQLITE@ SRTP=@PBX_SRTP@ SS7=@PBX_SS7@ OPENSSL=@PBX_OPENSSL@ diff --git a/configs/samples/res_config_sqlite.conf.sample b/configs/samples/res_config_sqlite.conf.sample deleted file mode 100644 index 2d14d46a3d..0000000000 --- a/configs/samples/res_config_sqlite.conf.sample +++ /dev/null @@ -1,11 +0,0 @@ -[general] - -; The database file. -dbfile => /var/lib/asterisk/sqlite.db - -; Both config_table and cdr_table are optional. If config_table is omitted, -; you must specify it in extconfig.conf. If it is both provided here and in -; extconfig.conf, the value given here is used. If cdr_table is omitted, CDR -; support is simply disabled. -config_table => ast_config -; cdr_table => ast_cdr diff --git a/configure b/configure index f6ca6eaee7..7d45eb869a 100755 --- a/configure +++ b/configure @@ -780,10 +780,6 @@ PBX_SQLITE3 SQLITE3_DIR SQLITE3_INCLUDE SQLITE3_LIB -PBX_SQLITE -SQLITE_DIR -SQLITE_INCLUDE -SQLITE_LIB PBX_SPEEXDSP SPEEXDSP_DIR SPEEXDSP_INCLUDE @@ -1412,7 +1408,6 @@ with_spandsp with_ss7 with_speex with_speexdsp -with_sqlite with_sqlite3 with_srtp with_ssl @@ -2181,7 +2176,6 @@ Optional Packages: --with-speex=PATH use Speex files in PATH --with-speex=PATH use Speex preprocess routines files in PATH --with-speexdsp=PATH use SpeexDSP files in PATH - --with-sqlite=PATH use SQLite files in PATH --with-sqlite3=PATH use SQLite files in PATH --with-srtp=PATH use Secure RTP files in PATH --with-ssl=PATH use OpenSSL Secure Sockets Layer files in PATH @@ -12047,38 +12041,6 @@ PBX_SPEEX_PREPROCESS=0 - SQLITE_DESCRIP="SQLite" - SQLITE_OPTION="sqlite" - PBX_SQLITE=0 - -# Check whether --with-sqlite was given. -if test "${with_sqlite+set}" = set; then : - withval=$with_sqlite; - case ${withval} in - n|no) - USE_SQLITE=no - # -1 is a magic value used by menuselect to know that the package - # was disabled, other than 'not found' - PBX_SQLITE=-1 - ;; - y|ye|yes) - ac_mandatory_list="${ac_mandatory_list} SQLITE" - ;; - *) - SQLITE_DIR="${withval}" - ac_mandatory_list="${ac_mandatory_list} SQLITE" - ;; - esac - -fi - - - - - - - - SQLITE3_DESCRIP="SQLite" SQLITE3_OPTION="sqlite3" PBX_SQLITE3=0 @@ -29658,103 +29620,6 @@ fi -if test "x${PBX_SQLITE}" != "x1" -a "${USE_SQLITE}" != "no"; then - pbxlibdir="" - # if --with-SQLITE=DIR has been specified, use it. - if test "x${SQLITE_DIR}" != "x"; then - if test -d ${SQLITE_DIR}/lib; then - pbxlibdir="-L${SQLITE_DIR}/lib" - else - pbxlibdir="-L${SQLITE_DIR}" - fi - fi - - ast_ext_lib_check_save_CFLAGS="${CFLAGS}" - CFLAGS="${CFLAGS} " - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite_exec in -lsqlite" >&5 -$as_echo_n "checking for sqlite_exec in -lsqlite... " >&6; } -if ${ac_cv_lib_sqlite_sqlite_exec+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsqlite ${pbxlibdir} $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char sqlite_exec (); -int -main () -{ -return sqlite_exec (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_sqlite_sqlite_exec=yes -else - ac_cv_lib_sqlite_sqlite_exec=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite_sqlite_exec" >&5 -$as_echo "$ac_cv_lib_sqlite_sqlite_exec" >&6; } -if test "x$ac_cv_lib_sqlite_sqlite_exec" = xyes; then : - AST_SQLITE_FOUND=yes -else - AST_SQLITE_FOUND=no -fi - - CFLAGS="${ast_ext_lib_check_save_CFLAGS}" - - - # now check for the header. - if test "${AST_SQLITE_FOUND}" = "yes"; then - SQLITE_LIB="${pbxlibdir} -lsqlite " - # if --with-SQLITE=DIR has been specified, use it. - if test "x${SQLITE_DIR}" != "x"; then - SQLITE_INCLUDE="-I${SQLITE_DIR}/include" - fi - SQLITE_INCLUDE="${SQLITE_INCLUDE} " - - # check for the header - ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" - CPPFLAGS="${CPPFLAGS} ${SQLITE_INCLUDE}" - ac_fn_c_check_header_mongrel "$LINENO" "sqlite.h" "ac_cv_header_sqlite_h" "$ac_includes_default" -if test "x$ac_cv_header_sqlite_h" = xyes; then : - SQLITE_HEADER_FOUND=1 -else - SQLITE_HEADER_FOUND=0 -fi - - - CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" - - if test "x${SQLITE_HEADER_FOUND}" = "x0" ; then - SQLITE_LIB="" - SQLITE_INCLUDE="" - else - - PBX_SQLITE=1 - cat >>confdefs.h <<_ACEOF -#define HAVE_SQLITE 1 -_ACEOF - - fi - fi -fi - - - - if test "x${PBX_SQLITE3}" != "x1" -a "${USE_SQLITE3}" != "no"; then pbxlibdir="" # if --with-SQLITE3=DIR has been specified, use it. diff --git a/configure.ac b/configure.ac index 13e1c1e8a5..ea6080dd3a 100644 --- a/configure.ac +++ b/configure.ac @@ -582,7 +582,6 @@ AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex]) AST_EXT_LIB_SETUP([SPEEX_PREPROCESS], [Speex preprocess routines], [speex]) AST_EXT_LIB_SETUP([SPEEXDSP], [SpeexDSP], [speexdsp]) AST_EXT_LIB_SETUP_DEPENDENT([SPEEX_PREPROCESS], [speex_preprocess_ctl], [], [speex]) -AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite]) AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3]) AST_EXT_LIB_SETUP([SRTP], [Secure RTP], [srtp]) AST_EXT_LIB_SETUP_OPTIONAL([SRTP_256], [SRTP Library AES-256 (ICM)], [SRTP], [srtp]) @@ -2584,8 +2583,6 @@ fi AC_SUBST(PBX_SPEEX_PREPROCESS) -AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h]) - AST_EXT_LIB_CHECK([SQLITE3], [sqlite3], [sqlite3_open], [sqlite3.h], [${PTHREAD_LIBS}], [${PTHREAD_CFLAGS}]) if test "${PBX_SQLITE3}" != 1; then diff --git a/doc/UPGRADE-staging/res_config_sqlite_removal.txt b/doc/UPGRADE-staging/res_config_sqlite_removal.txt new file mode 100644 index 0000000000..13c259d789 --- /dev/null +++ b/doc/UPGRADE-staging/res_config_sqlite_removal.txt @@ -0,0 +1,6 @@ +Subject: res_config_sqlite +Master-Only: True + +This module was deprecated in Asterisk 16 +and is now being removed in accordance with +the Asterisk Module Deprecation policy. diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index e56f1c85c3..fc3a9bdb00 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -905,9 +905,6 @@ /* Define to 1 if you have the speex_preprocess_ctl library. */ #undef HAVE_SPEEX_PREPROCESS -/* Define to 1 if you have the SQLite library. */ -#undef HAVE_SQLITE - /* Define to 1 if you have the SQLite library. */ #undef HAVE_SQLITE3 diff --git a/res/res_config_sqlite.c b/res/res_config_sqlite.c deleted file mode 100644 index 232600018c..0000000000 --- a/res/res_config_sqlite.c +++ /dev/null @@ -1,1789 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2006, Proformatique - * - * Written by Richard Braun - * - * Based on res_sqlite3 by Anthony Minessale II, - * and res_config_mysql by Matthew Boehm - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! - * \page res_config_sqlite - * - * \section intro_sec Presentation - * - * res_config_sqlite is a module for the Asterisk Open Source PBX to - * support SQLite 2 databases. It can be used to fetch configuration - * from a database (static configuration files and/or using the Asterisk - * RealTime Architecture - ARA). It can also be used to log CDR entries. - * Note that Asterisk already comes with a module 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 - * simple module for Asterisk, the other is to have the same database open for - * all kinds of operations, which improves reliability and performance. - * - * \section conf_sec Configuration - * - * The main configuration file is res_config_sqlite.conf.sample It must be readable or - * 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 - * named general. Here are the supported parameters : - * - *
- *
dbfile
- *
The absolute path to the SQLite database (the file can be non existent, - * res_config_sqlite will create it if it has the appropriate rights)
- *
config_table
- *
The table used for static configuration
- *
cdr_table
- *
The table used to store CDR entries (if ommitted, CDR support is - * disabled)
- *
- * - * 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 - * needed tables. - * - * \section status_sec Driver status - * - * The CLI command show sqlite status returns status information - * about the running driver. - * - * \section credits_sec Credits - * - * res_config_sqlite was developed by Richard Braun at the Proformatique company. - */ - -/*! - * \file - * \brief res_config_sqlite module. - */ - -/*! \li \ref res_config_sqlite.c uses the configuration file \ref res_config_sqlite.conf - * \addtogroup configuration_file Configuration Files - */ - -/*! - * \page res_config_sqlite.conf res_config_sqlite.conf - * \verbinclude res_config_sqlite.conf.sample - */ - -/*** MODULEINFO - sqlite - deprecated - 16 - 19 - ***/ - -#include "asterisk.h" - -#include - -#include "asterisk/logger.h" -#include "asterisk/app.h" -#include "asterisk/pbx.h" -#include "asterisk/cdr.h" -#include "asterisk/cli.h" -#include "asterisk/lock.h" -#include "asterisk/config.h" -#include "asterisk/module.h" -#include "asterisk/linkedlists.h" - -#define MACRO_BEGIN do { -#define MACRO_END } while (0) - -#define RES_CONFIG_SQLITE_NAME "res_config_sqlite" -#define RES_CONFIG_SQLITE_DRIVER "sqlite" -#define RES_CONFIG_SQLITE_DESCRIPTION "Resource Module for SQLite 2" -#define RES_CONFIG_SQLITE_CONF_FILE "res_config_sqlite.conf" - -enum { - RES_CONFIG_SQLITE_CONFIG_ID, - RES_CONFIG_SQLITE_CONFIG_CAT_METRIC, - RES_CONFIG_SQLITE_CONFIG_VAR_METRIC, - RES_CONFIG_SQLITE_CONFIG_COMMENTED, - RES_CONFIG_SQLITE_CONFIG_FILENAME, - RES_CONFIG_SQLITE_CONFIG_CATEGORY, - RES_CONFIG_SQLITE_CONFIG_VAR_NAME, - RES_CONFIG_SQLITE_CONFIG_VAR_VAL, - RES_CONFIG_SQLITE_CONFIG_COLUMNS, -}; - -#define SET_VAR(config, to, from) \ -MACRO_BEGIN \ - int __error; \ - \ - __error = set_var(&to, #to, from->value); \ - \ - if (__error) { \ - ast_config_destroy(config); \ - unload_config(); \ - return 1; \ - } \ -MACRO_END - -AST_THREADSTORAGE(sql_buf); -AST_THREADSTORAGE(where_buf); - -/*! - * Maximum number of loops before giving up executing a query. Calls to - * sqlite_xxx() functions which can return SQLITE_BUSY - * are enclosed by RES_CONFIG_SQLITE_BEGIN and RES_CONFIG_SQLITE_END, e.g. - *
- * char *errormsg;
- * int error;
- *
- * RES_CONFIG_SQLITE_BEGIN
- *	 error = sqlite_exec(db, query, NULL, NULL, &errormsg);
- * RES_CONFIG_SQLITE_END(error)
- *
- * if (error)
- *	 ...;
- * 
- */ -#define RES_CONFIG_SQLITE_MAX_LOOPS 10 - -/*! - * Macro used before executing a query. - * - * \see RES_CONFIG_SQLITE_MAX_LOOPS. - */ -#define RES_CONFIG_SQLITE_BEGIN \ -MACRO_BEGIN \ - int __i; \ - \ - for (__i = 0; __i < RES_CONFIG_SQLITE_MAX_LOOPS; __i++) { - -/*! - * Macro used after executing a query. - * - * \see RES_CONFIG_SQLITE_MAX_LOOPS. - */ -#define RES_CONFIG_SQLITE_END(error) \ - if (error != SQLITE_BUSY) \ - break; \ - usleep(1000); \ - } \ -MACRO_END; - -/*! - * Structure sent to the SQLite callback function for static configuration. - * - * \see add_cfg_entry() - */ -struct cfg_entry_args { - struct ast_config *cfg; - struct ast_category *cat; - char *cat_name; - struct ast_flags flags; - const char *who_asked; -}; - -/*! - * Structure sent to the SQLite callback function for RealTime configuration. - * - * \see add_rt_cfg_entry() - */ -struct rt_cfg_entry_args { - struct ast_variable *var; - struct ast_variable *last; -}; - -/*! - * Structure sent to the SQLite callback function for RealTime configuration - * (realtime_multi_handler()). - * - * \see add_rt_multi_cfg_entry() - */ -struct rt_multi_cfg_entry_args { - struct ast_config *cfg; - char *initfield; -}; - -/*! - * \brief Allocate a variable. - * \param var the address of the variable to set (it will be allocated) - * \param name the name of the variable (for error handling) - * \param value the value to store in var - * \retval 0 on success - * \retval 1 if an allocation error occurred - */ -static int set_var(char **var, const char *name, const char *value); - -/*! - * \brief Load the configuration file. - * \see unload_config() - * - * This function sets dbfile, config_table, and cdr_table. It calls - * check_vars() before returning, and unload_config() if an error occurred. - * - * \retval 0 on success - * \retval 1 if an error occurred - */ -static int load_config(void); - -/*! - * \brief Free resources related to configuration. - * \see load_config() - */ -static void unload_config(void); - -/*! - * \brief Asterisk callback function for CDR support. - * \param cdr the CDR entry Asterisk sends us. - * - * Asterisk will call this function each time a CDR entry must be logged if - * CDR support is enabled. - * - * \retval 0 on success - * \retval 1 if an error occurred - */ -static int cdr_handler(struct ast_cdr *cdr); - -/*! - * \brief SQLite callback function for static configuration. - * - * 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 - * resulting rows being sorted by category. - * - * \param arg a pointer to a struct cfg_entry_args object - * \param argc number of columns - * \param argv values in the row - * \param columnNames names and types of the columns - * \retval 0 on success - * \retval 1 if an error occurred - * \see cfg_entry_args - * \see sql_get_config_table - * \see config_handler() - */ -static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames); - -/*! - * \brief Asterisk callback function for static configuration. - * - * Asterisk will call this function when it loads its static configuration, - * which usually happens at startup and reload. - * - * \param database the database to use (ignored) - * \param table the table to use - * \param file the file to load from the database - * \param cfg the struct ast_config object to use when storing variables - * \param flags Optional flags. Not used. - * \param suggested_incl suggest include. - * \param who_asked - * \retval cfg object - * \retval NULL if an error occurred - * \see add_cfg_entry() - */ -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, const char *who_asked); - -/*! - * \brief SQLite callback function for RealTime configuration. - * - * This function is passed to the SQLite engine as a callback function to - * parse a row and store it in a linked list of struct ast_variable objects. - * - * \param arg a pointer to a struct rt_cfg_entry_args object - * \param argc number of columns - * \param argv values in the row - * \param columnNames names and types of the columns - * \retval 0 on success. - * \retval 1 if an error occurred. - * \see rt_cfg_entry_args - * \see realtime_handler() - */ -static int add_rt_cfg_entry(void *arg, int argc, char **argv, - char **columnNames); - -/*! - * \brief Asterisk callback function for RealTime configuration. - * - * Asterisk will call this function each time it requires a variable - * through the RealTime architecture. ap is a list of parameters and - * values used to find a specific row, e.g one parameter "name" and - * one value "123" so that the SQL query becomes SELECT * FROM - * table WHERE name = '123';. - * - * \param database the database to use (ignored) - * \param table the table to use - * \param fields list of parameters and values to match - * - * \retval a linked list of struct ast_variable objects - * \retval NULL if an error occurred - * \see add_rt_cfg_entry() - */ -static struct ast_variable * realtime_handler(const char *database, - const char *table, const struct ast_variable *fields); - -/*! - * \brief SQLite callback function for RealTime configuration. - * - * This function performs the same actions as add_rt_cfg_entry() except - * that the rt_multi_cfg_entry_args structure is designed to store - * categories in addition to variables. - * - * \param arg a pointer to a struct rt_multi_cfg_entry_args object - * \param argc number of columns - * \param argv values in the row - * \param columnNames names and types of the columns - * \retval 0 on success. - * \retval 1 if an error occurred. - * \see rt_multi_cfg_entry_args - * \see realtime_multi_handler() - */ -static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, - char **columnNames); - -/*! - * \brief Asterisk callback function for RealTime configuration. - * - * This function performs the same actions as realtime_handler() except - * that it can store variables per category, and can return several - * categories. - * - * \param database the database to use (ignored) - * \param table the table to use - * \param fields list of parameters and values to match - * \retval a struct ast_config object storing categories and variables. - * \retval NULL if an error occurred. - * - * \see add_rt_multi_cfg_entry() - */ -static struct ast_config * realtime_multi_handler(const char *database, - const char *table, const struct ast_variable *fields); - -/*! - * \brief Asterisk callback function for RealTime configuration (variable - * update). - * - * Asterisk will call this function each time a variable has been modified - * internally and must be updated in the backend engine. keyfield and entity - * are used to find the row to update, e.g. UPDATE table SET ... WHERE - * keyfield = 'entity';. ap is a list of parameters and values with the - * same format as the other realtime functions. - * - * \param database the database to use (ignored) - * \param table the table to use - * \param keyfield the column of the matching cell - * \param entity the value of the matching cell - * \param fields list of parameters and new values to update in the database - * \retval the number of affected rows. - * \retval -1 if an error occurred. - */ -static int realtime_update_handler(const char *database, const char *table, - const char *keyfield, const char *entity, const struct ast_variable *fields); -static int realtime_update2_handler(const char *database, const char *table, - const struct ast_variable *lookup_fields, const struct ast_variable *update_fields); - -/*! - * \brief Asterisk callback function for RealTime configuration (variable - * create/store). - * - * Asterisk will call this function each time a variable has been created - * internally and must be stored in the backend engine. - * are used to find the row to update, e.g. ap is a list of parameters and - * values with the same format as the other realtime functions. - * - * \param database the database to use (ignored) - * \param table the table to use - * \param fields list of parameters and new values to insert into the database - * \retval the rowid of inserted row. - * \retval -1 if an error occurred. - */ -static int realtime_store_handler(const char *database, const char *table, - const struct ast_variable *fields); - -/*! - * \brief Asterisk callback function for RealTime configuration (destroys - * variable). - * - * Asterisk will call this function each time a variable has been destroyed - * internally and must be removed from the backend engine. keyfield and entity - * are used to find the row to delete, e.g. DELETE FROM table WHERE - * keyfield = 'entity';. ap is a list of parameters and values with the - * same format as the other realtime functions. - * - * \param database the database to use (ignored) - * \param table the table to use - * \param keyfield the column of the matching cell - * \param entity the value of the matching cell - * \param fields list of additional parameters for cell matching - * \retval the number of affected rows. - * \retval -1 if an error occurred. - */ -static int realtime_destroy_handler(const char *database, const char *table, - const char *keyfield, const char *entity, const struct ast_variable *fields); - -/*! - * \brief Asterisk callback function for the CLI status command. - * - * \param e CLI command - * \param cmd - * \param a CLI argument list - * \return RESULT_SUCCESS - */ -static char *handle_cli_show_sqlite_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -static char *handle_cli_sqlite_show_tables(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); - -static int realtime_require_handler(const char *database, const char *table, va_list ap); -static int realtime_unload_handler(const char *unused, const char *tablename); - -/*! The SQLite database object. */ -static sqlite *db; - -/*! Set to 1 if CDR support is enabled. */ -static int use_cdr; - -/*! Set to 1 if the CDR callback function was registered. */ -static int cdr_registered; - -/*! Set to 1 if the CLI status command callback function was registered. */ -static int cli_status_registered; - -/*! The path of the database file. */ -static char *dbfile; - -/*! The name of the static configuration table. */ -static char *config_table; - -/*! The name of the table used to store CDR entries. */ -static char *cdr_table; - -/*! - * The structure specifying all callback functions used by Asterisk for static - * and RealTime configuration. - */ -static struct ast_config_engine sqlite_engine = -{ - .name = RES_CONFIG_SQLITE_DRIVER, - .load_func = config_handler, - .realtime_func = realtime_handler, - .realtime_multi_func = realtime_multi_handler, - .store_func = realtime_store_handler, - .destroy_func = realtime_destroy_handler, - .update_func = realtime_update_handler, - .update2_func = realtime_update2_handler, - .require_func = realtime_require_handler, - .unload_func = realtime_unload_handler, -}; - -/*! - * The mutex used to prevent simultaneous access to the SQLite database. - */ -AST_MUTEX_DEFINE_STATIC(mutex); - -/*! - * Structure containing details and callback functions for the CLI status - * command. - */ -static struct ast_cli_entry cli_status[] = { - AST_CLI_DEFINE(handle_cli_show_sqlite_status, "Show status information about the SQLite 2 driver"), - AST_CLI_DEFINE(handle_cli_sqlite_show_tables, "Cached table information about the SQLite 2 driver"), -}; - -struct sqlite_cache_columns { - char *name; - char *type; - unsigned char isint; /*!< By definition, only INTEGER PRIMARY KEY is an integer; everything else is a string. */ - AST_RWLIST_ENTRY(sqlite_cache_columns) list; -}; - -struct sqlite_cache_tables { - char *name; - AST_RWLIST_HEAD(_columns, sqlite_cache_columns) columns; - AST_RWLIST_ENTRY(sqlite_cache_tables) list; -}; - -static AST_RWLIST_HEAD_STATIC(sqlite_tables, sqlite_cache_tables); - -/* - * Taken from Asterisk 1.2 cdr_sqlite.so. - */ - -/*! SQL query format to create the CDR table if non existent. */ -static char *sql_create_cdr_table = -"CREATE TABLE '%q' (\n" -" id INTEGER,\n" -" clid VARCHAR(80) NOT NULL DEFAULT '',\n" -" src VARCHAR(80) NOT NULL DEFAULT '',\n" -" dst VARCHAR(80) NOT NULL DEFAULT '',\n" -" dcontext VARCHAR(80) NOT NULL DEFAULT '',\n" -" channel VARCHAR(80) NOT NULL DEFAULT '',\n" -" dstchannel VARCHAR(80) NOT NULL DEFAULT '',\n" -" lastapp VARCHAR(80) NOT NULL DEFAULT '',\n" -" lastdata VARCHAR(80) NOT NULL DEFAULT '',\n" -" start DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n" -" answer DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n" -" end DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',\n" -" duration INT(11) NOT NULL DEFAULT 0,\n" -" billsec INT(11) NOT NULL DEFAULT 0,\n" -" disposition VARCHAR(45) NOT NULL DEFAULT '',\n" -" amaflags INT(11) NOT NULL DEFAULT 0,\n" -" accountcode VARCHAR(20) NOT NULL DEFAULT '',\n" -" uniqueid VARCHAR(32) NOT NULL DEFAULT '',\n" -" userfield VARCHAR(255) NOT NULL DEFAULT '',\n" -" PRIMARY KEY (id)\n" -");"; - -/*! - * SQL query format to describe the table structure - */ -#define sql_table_structure "SELECT sql FROM sqlite_master WHERE type='table' AND tbl_name='%s'" - -/*! - * SQL query format to fetch the static configuration of a file. - * Rows must be sorted by category. - * - * \see add_cfg_entry() - */ -#define sql_get_config_table \ - "SELECT *" \ - " FROM '%q'" \ - " WHERE filename = '%q' AND commented = 0" \ - " ORDER BY cat_metric ASC, var_metric ASC;" - -static void free_table(struct sqlite_cache_tables *tblptr) -{ - struct sqlite_cache_columns *col; - - /* Obtain a write lock to ensure there are no read locks outstanding */ - AST_RWLIST_WRLOCK(&(tblptr->columns)); - while ((col = AST_RWLIST_REMOVE_HEAD(&(tblptr->columns), list))) { - ast_free(col); - } - AST_RWLIST_UNLOCK(&(tblptr->columns)); - AST_RWLIST_HEAD_DESTROY(&(tblptr->columns)); - ast_free(tblptr); -} - -static int find_table_cb(void *vtblptr, int argc, char **argv, char **columnNames) -{ - struct sqlite_cache_tables *tblptr = vtblptr; - char *sql = ast_strdupa(argv[0]), *start, *end, *type, *remainder; - int i; - AST_DECLARE_APP_ARGS(fie, - AST_APP_ARG(ld)[100]; /* This means we support up to 100 columns per table */ - ); - struct sqlite_cache_columns *col; - - /* This is really fun. We get to parse an SQL statement to figure out - * what columns are in the table. - */ - if ((start = strchr(sql, '(')) && (end = strrchr(sql, ')'))) { - start++; - *end = '\0'; - } else { - /* Abort */ - return -1; - } - - AST_STANDARD_APP_ARGS(fie, start); - for (i = 0; i < fie.argc; i++) { - fie.ld[i] = ast_skip_blanks(fie.ld[i]); - ast_debug(5, "Found field: %s\n", fie.ld[i]); - if (strncasecmp(fie.ld[i], "PRIMARY KEY", 11) == 0 && (start = strchr(fie.ld[i], '(')) && (end = strchr(fie.ld[i], ')'))) { - *end = '\0'; - AST_RWLIST_TRAVERSE(&(tblptr->columns), col, list) { - if (strcasecmp(start + 1, col->name) == 0 && strcasestr(col->type, "INTEGER")) { - col->isint = 1; - } - } - continue; - } - /* type delimiter could be any space character */ - for (type = fie.ld[i]; *type > 32; type++); - *type++ = '\0'; - type = ast_skip_blanks(type); - for (remainder = type; *remainder > 32; remainder++); - *remainder = '\0'; - if (!(col = ast_calloc(1, sizeof(*col) + strlen(fie.ld[i]) + strlen(type) + 2))) { - return -1; - } - col->name = (char *)col + sizeof(*col); - col->type = (char *)col + sizeof(*col) + strlen(fie.ld[i]) + 1; - strcpy(col->name, fie.ld[i]); /* SAFE */ - strcpy(col->type, type); /* SAFE */ - if (strcasestr(col->type, "INTEGER") && strcasestr(col->type, "PRIMARY KEY")) { - col->isint = 1; - } - AST_LIST_INSERT_TAIL(&(tblptr->columns), col, list); - } - return 0; -} - -static struct sqlite_cache_tables *find_table(const char *tablename) -{ - struct sqlite_cache_tables *tblptr; - int i, err; - char *sql, *errstr = NULL; - - AST_RWLIST_RDLOCK(&sqlite_tables); - - for (i = 0; i < 2; i++) { - AST_RWLIST_TRAVERSE(&sqlite_tables, tblptr, list) { - if (strcmp(tblptr->name, tablename) == 0) { - break; - } - } - if (tblptr) { - AST_RWLIST_RDLOCK(&(tblptr->columns)); - AST_RWLIST_UNLOCK(&sqlite_tables); - return tblptr; - } - - if (i == 0) { - AST_RWLIST_UNLOCK(&sqlite_tables); - AST_RWLIST_WRLOCK(&sqlite_tables); - } - } - - /* Table structure not cached; build the structure now */ - if (ast_asprintf(&sql, sql_table_structure, tablename) < 0) { - sql = NULL; - } - if (!(tblptr = ast_calloc(1, sizeof(*tblptr) + strlen(tablename) + 1))) { - AST_RWLIST_UNLOCK(&sqlite_tables); - ast_log(LOG_ERROR, "Memory error. Cannot cache table '%s'\n", tablename); - ast_free(sql); - return NULL; - } - tblptr->name = (char *)tblptr + sizeof(*tblptr); - strcpy(tblptr->name, tablename); /* SAFE */ - AST_RWLIST_HEAD_INIT(&(tblptr->columns)); - - ast_debug(1, "About to query table structure: %s\n", sql); - - ast_mutex_lock(&mutex); - if ((err = sqlite_exec(db, sql, find_table_cb, tblptr, &errstr))) { - ast_mutex_unlock(&mutex); - ast_log(LOG_WARNING, "SQLite error %d: %s\n", err, errstr); - ast_free(errstr); - free_table(tblptr); - AST_RWLIST_UNLOCK(&sqlite_tables); - ast_free(sql); - return NULL; - } - ast_mutex_unlock(&mutex); - ast_free(sql); - - if (AST_LIST_EMPTY(&(tblptr->columns))) { - free_table(tblptr); - AST_RWLIST_UNLOCK(&sqlite_tables); - return NULL; - } - - AST_RWLIST_INSERT_TAIL(&sqlite_tables, tblptr, list); - AST_RWLIST_RDLOCK(&(tblptr->columns)); - AST_RWLIST_UNLOCK(&sqlite_tables); - return tblptr; -} - -#define release_table(a) AST_RWLIST_UNLOCK(&((a)->columns)) - -static int set_var(char **var, const char *name, const char *value) -{ - if (*var) - ast_free(*var); - - *var = ast_strdup(value); - - if (!*var) { - ast_log(LOG_WARNING, "Unable to allocate variable %s\n", name); - return 1; - } - - return 0; -} - -static int check_vars(void) -{ - if (!dbfile) { - ast_log(LOG_ERROR, "Required parameter undefined: dbfile\n"); - return 1; - } - - use_cdr = (cdr_table != NULL); - - return 0; -} - -static int load_config(void) -{ - struct ast_config *config; - struct ast_variable *var; - int error; - struct ast_flags config_flags = { 0 }; - - config = ast_config_load(RES_CONFIG_SQLITE_CONF_FILE, config_flags); - - if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) { - ast_log(LOG_ERROR, "Unable to load " RES_CONFIG_SQLITE_CONF_FILE "\n"); - return 1; - } - - for (var = ast_variable_browse(config, "general"); var; var = var->next) { - if (!strcasecmp(var->name, "dbfile")) - SET_VAR(config, dbfile, var); - else if (!strcasecmp(var->name, "config_table")) - SET_VAR(config, config_table, var); - else if (!strcasecmp(var->name, "cdr_table")) { - SET_VAR(config, cdr_table, var); - } else - ast_log(LOG_WARNING, "Unknown parameter : %s\n", var->name); - } - - ast_config_destroy(config); - error = check_vars(); - - if (error) { - unload_config(); - return 1; - } - - return 0; -} - -static void unload_config(void) -{ - struct sqlite_cache_tables *tbl; - ast_free(dbfile); - dbfile = NULL; - ast_free(config_table); - config_table = NULL; - ast_free(cdr_table); - cdr_table = NULL; - AST_RWLIST_WRLOCK(&sqlite_tables); - while ((tbl = AST_RWLIST_REMOVE_HEAD(&sqlite_tables, list))) { - free_table(tbl); - } - AST_RWLIST_UNLOCK(&sqlite_tables); -} - -static int cdr_handler(struct ast_cdr *cdr) -{ - char *errormsg = NULL, *tmp, workspace[500]; - int error, scannum; - struct sqlite_cache_tables *tbl = find_table(cdr_table); - struct sqlite_cache_columns *col; - struct ast_str *sql1 = ast_str_create(160), *sql2 = ast_str_create(16); - int first = 1; - - if (!sql1 || !sql2) { - ast_free(sql1); - ast_free(sql2); - return -1; - } - - if (!tbl) { - ast_log(LOG_WARNING, "No such table: %s\n", cdr_table); - ast_free(sql1); - ast_free(sql2); - return -1; - } - - ast_str_set(&sql1, 0, "INSERT INTO %s (", cdr_table); - ast_str_set(&sql2, 0, ") VALUES ("); - - AST_RWLIST_TRAVERSE(&(tbl->columns), col, list) { - if (col->isint) { - ast_cdr_format_var(cdr, col->name, &tmp, workspace, sizeof(workspace), 1); - if (!tmp) { - continue; - } - if (sscanf(tmp, "%30d", &scannum) == 1) { - ast_str_append(&sql1, 0, "%s%s", first ? "" : ",", col->name); - ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", scannum); - } - } else { - ast_cdr_format_var(cdr, col->name, &tmp, workspace, sizeof(workspace), 0); - if (!tmp) { - continue; - } - ast_str_append(&sql1, 0, "%s%s", first ? "" : ",", col->name); - tmp = sqlite_mprintf("%Q", tmp); - ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", tmp); - sqlite_freemem(tmp); - } - first = 0; - } - release_table(tbl); - - ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2)); - ast_free(sql2); - - ast_debug(1, "SQL query: %s\n", ast_str_buffer(sql1)); - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, ast_str_buffer(sql1), NULL, NULL, &errormsg); - RES_CONFIG_SQLITE_END(error) - - ast_mutex_unlock(&mutex); - - ast_free(sql1); - - if (error) { - ast_log(LOG_ERROR, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - return 1; - } - sqlite_freemem(errormsg); - - return 0; -} - -static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames) -{ - struct cfg_entry_args *args; - struct ast_variable *var; - - if (argc != RES_CONFIG_SQLITE_CONFIG_COLUMNS) { - ast_log(LOG_WARNING, "Corrupt table\n"); - return 1; - } - - args = arg; - - if (!strcmp(argv[RES_CONFIG_SQLITE_CONFIG_VAR_NAME], "#include")) { - struct ast_config *cfg; - char *val; - - val = argv[RES_CONFIG_SQLITE_CONFIG_VAR_VAL]; - cfg = ast_config_internal_load(val, args->cfg, args->flags, "", args->who_asked); - - if (!cfg) { - ast_log(LOG_WARNING, "Unable to include %s\n", val); - return 1; - } else { - args->cfg = cfg; - return 0; - } - } - - if (!args->cat_name || strcmp(args->cat_name, argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY])) { - args->cat = ast_category_new_dynamic(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY]); - if (!args->cat) { - return 1; - } - - ast_free(args->cat_name); - args->cat_name = ast_strdup(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY]); - - if (!args->cat_name) { - ast_category_destroy(args->cat); - return 1; - } - - ast_category_append(args->cfg, args->cat); - } - - var = ast_variable_new(argv[RES_CONFIG_SQLITE_CONFIG_VAR_NAME], argv[RES_CONFIG_SQLITE_CONFIG_VAR_VAL], ""); - - if (!var) { - ast_log(LOG_WARNING, "Unable to allocate variable\n"); - return 1; - } - - ast_variable_append(args->cat, var); - - return 0; -} - -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, const char *who_asked) -{ - struct cfg_entry_args args; - char *query, *errormsg = NULL; - int error; - - if (!config_table) { - if (!table) { - ast_log(LOG_ERROR, "Table name unspecified\n"); - return NULL; - } - } else - 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.cat = NULL; - args.cat_name = NULL; - args.flags = flags; - args.who_asked = who_asked; - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, query, add_cfg_entry, &args, &errormsg); - RES_CONFIG_SQLITE_END(error) - - ast_mutex_unlock(&mutex); - - ast_free(args.cat_name); - sqlite_freemem(query); - - if (error) { - ast_log(LOG_ERROR, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - return NULL; - } - sqlite_freemem(errormsg); - - return cfg; -} - -static int add_rt_cfg_entry(void *arg, int argc, char **argv, char **columnNames) -{ - struct rt_cfg_entry_args *args; - struct ast_variable *var; - int i; - - args = arg; - - for (i = 0; i < argc; i++) { - if (!argv[i]) - continue; - - if (!(var = ast_variable_new(columnNames[i], argv[i], ""))) - return 1; - - if (!args->var) - args->var = var; - - if (!args->last) - args->last = var; - else { - args->last->next = var; - args->last = var; - } - } - - return 0; -} - -static struct ast_variable * realtime_handler(const char *database, const char *table, const struct ast_variable *fields) -{ - char *query, *errormsg = NULL, *op, *tmp_str; - struct rt_cfg_entry_args args; - const struct ast_variable *field = fields; - int error; - - if (!table) { - ast_log(LOG_WARNING, "Table name unspecified\n"); - return NULL; - } - - if (!fields) { - return NULL; - } - - op = (strchr(field->name, ' ') == NULL) ? " =" : ""; - -/* \cond DOXYGEN_CAN_PARSE_THIS */ -#undef QUERY -#define QUERY "SELECT * FROM '%q' WHERE%s %q%s '%q'" -/* \endcond */ - - query = sqlite_mprintf(QUERY, table, (config_table && !strcmp(config_table, table)) ? " commented = 0 AND" : "", field->name, op, field->value); - - if (!query) { - ast_log(LOG_WARNING, "Unable to allocate SQL query\n"); - return NULL; - } - - while ((field = field->next)) { - op = (strchr(field->name, ' ') == NULL) ? " =" : ""; - tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, field->name, op, field->value); - sqlite_freemem(query); - - if (!tmp_str) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - return NULL; - } - - query = tmp_str; - } - - tmp_str = sqlite_mprintf("%s LIMIT 1;", query); - sqlite_freemem(query); - - if (!tmp_str) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - return NULL; - } - - query = tmp_str; - ast_debug(1, "SQL query: %s\n", query); - args.var = NULL; - args.last = NULL; - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, query, add_rt_cfg_entry, &args, &errormsg); - RES_CONFIG_SQLITE_END(error) - - ast_mutex_unlock(&mutex); - - sqlite_freemem(query); - - if (error) { - ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - ast_variables_destroy(args.var); - return NULL; - } - sqlite_freemem(errormsg); - - return args.var; -} - -static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, char **columnNames) -{ - struct rt_multi_cfg_entry_args *args; - struct ast_category *cat; - struct ast_variable *var; - char *cat_name; - size_t i; - - args = arg; - cat_name = NULL; - - /* - * cat_name should always be set here, since initfield is forged from - * params[0] in realtime_multi_handler(), which is a search parameter - * of the SQL query. - */ - for (i = 0; i < argc; i++) { - if (!strcmp(args->initfield, columnNames[i])) - cat_name = argv[i]; - } - - if (!cat_name) { - ast_log(LOG_ERROR, "Bogus SQL results, cat_name is NULL !\n"); - return 1; - } - - cat = ast_category_new_dynamic(cat_name); - if (!cat) { - return 1; - } - - ast_category_append(args->cfg, cat); - - for (i = 0; i < argc; i++) { - if (!argv[i]) { - continue; - } - - if (!(var = ast_variable_new(columnNames[i], argv[i], ""))) { - ast_log(LOG_WARNING, "Unable to allocate variable\n"); - return 1; - } - - ast_variable_append(cat, var); - } - - return 0; -} - -static struct ast_config *realtime_multi_handler(const char *database, - const char *table, const struct ast_variable *fields) -{ - char *query, *errormsg = NULL, *op, *tmp_str, *initfield; - struct rt_multi_cfg_entry_args args; - const struct ast_variable *field = fields; - struct ast_config *cfg; - int error; - - if (!table) { - ast_log(LOG_WARNING, "Table name unspecified\n"); - return NULL; - } - - if (!fields) { - return NULL; - } - - if (!(cfg = ast_config_new())) { - ast_log(LOG_WARNING, "Unable to allocate configuration structure\n"); - return NULL; - } - - if (!(initfield = ast_strdup(field->name))) { - ast_config_destroy(cfg); - return NULL; - } - - tmp_str = strchr(initfield, ' '); - - if (tmp_str) - *tmp_str = '\0'; - - op = (!strchr(field->name, ' ')) ? " =" : ""; - - /* - * Asterisk sends us an already escaped string when searching for - * "exten LIKE" (uh!). Handle it separately. - */ - tmp_str = (!strcmp(field->value, "\\_%")) ? "_%" : (char *)field->value; - -/* \cond DOXYGEN_CAN_PARSE_THIS */ -#undef QUERY -#define QUERY "SELECT * FROM '%q' WHERE%s %q%s '%q'" -/* \endcond */ - - if (!(query = sqlite_mprintf(QUERY, table, (config_table && !strcmp(config_table, table)) ? " commented = 0 AND" : "", field->name, op, tmp_str))) { - ast_log(LOG_WARNING, "Unable to allocate SQL query\n"); - ast_config_destroy(cfg); - ast_free(initfield); - return NULL; - } - - while ((field = field->next)) { - op = (!strchr(field->name, ' ')) ? " =" : ""; - tmp_str = sqlite_mprintf("%s AND %q%s '%q'", query, field->name, op, field->value); - sqlite_freemem(query); - - if (!tmp_str) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - ast_config_destroy(cfg); - ast_free(initfield); - return NULL; - } - - query = tmp_str; - } - - if (!(tmp_str = sqlite_mprintf("%s ORDER BY %q;", query, initfield))) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - sqlite_freemem(query); - ast_config_destroy(cfg); - ast_free(initfield); - return NULL; - } - - sqlite_freemem(query); - query = tmp_str; - ast_debug(1, "SQL query: %s\n", query); - args.cfg = cfg; - args.initfield = initfield; - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, query, add_rt_multi_cfg_entry, &args, &errormsg); - RES_CONFIG_SQLITE_END(error) - - ast_mutex_unlock(&mutex); - - sqlite_freemem(query); - ast_free(initfield); - - if (error) { - ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - ast_config_destroy(cfg); - return NULL; - } - sqlite_freemem(errormsg); - - return cfg; -} - -static int realtime_update_handler(const char *database, const char *table, - const char *keyfield, const char *entity, const struct ast_variable *fields) -{ - char *query, *errormsg = NULL, *tmp_str; - const struct ast_variable *field = fields; - int error, rows_num; - - if (!table) { - ast_log(LOG_WARNING, "Table name unspecified\n"); - return -1; - } - - if (!field) { - return -1; - } - -/* \cond DOXYGEN_CAN_PARSE_THIS */ -#undef QUERY -#define QUERY "UPDATE '%q' SET %q = '%q'" -/* \endcond */ - - if (!(query = sqlite_mprintf(QUERY, table, field->name, field->value))) { - ast_log(LOG_WARNING, "Unable to allocate SQL query\n"); - return -1; - } - - while ((field = field->next)) { - tmp_str = sqlite_mprintf("%s, %q = '%q'", query, field->name, field->value); - sqlite_freemem(query); - - if (!tmp_str) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - return -1; - } - - query = tmp_str; - } - - if (!(tmp_str = sqlite_mprintf("%s WHERE %q = '%q';", query, keyfield, entity))) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - sqlite_freemem(query); - return -1; - } - - sqlite_freemem(query); - query = tmp_str; - ast_debug(1, "SQL query: %s\n", query); - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, query, NULL, NULL, &errormsg); - RES_CONFIG_SQLITE_END(error) - - if (!error) - rows_num = sqlite_changes(db); - else - rows_num = -1; - - ast_mutex_unlock(&mutex); - - sqlite_freemem(query); - - if (error) { - ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - } - sqlite_freemem(errormsg); - - return rows_num; -} - -static int realtime_update2_handler(const char *database, const char *table, - const struct ast_variable *lookup_fields, const struct ast_variable *update_fields) -{ - char *errormsg = NULL, *tmp1, *tmp2; - int error, rows_num, first = 1; - struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); - struct ast_str *where = ast_str_thread_get(&where_buf, 100); - const struct ast_variable *field; - - if (!table) { - ast_log(LOG_WARNING, "Table name unspecified\n"); - return -1; - } - - if (!sql) { - return -1; - } - - ast_str_set(&sql, 0, "UPDATE %s SET", table); - ast_str_set(&where, 0, " WHERE"); - - for (field = lookup_fields; field; field = field->next) { - ast_str_append(&where, 0, "%s %s = %s", - first ? "" : " AND", - tmp1 = sqlite_mprintf("%q", field->name), - tmp2 = sqlite_mprintf("%Q", field->value)); - sqlite_freemem(tmp1); - sqlite_freemem(tmp2); - first = 0; - } - - if (first) { - ast_log(LOG_ERROR, "No criteria specified on update to '%s@%s'!\n", table, database); - return -1; - } - - first = 1; - for (field = update_fields; field; field = field->next) { - ast_str_append(&sql, 0, "%s %s = %s", - first ? "" : ",", - tmp1 = sqlite_mprintf("%q", field->name), - tmp2 = sqlite_mprintf("%Q", field->value)); - sqlite_freemem(tmp1); - sqlite_freemem(tmp2); - first = 0; - } - - ast_str_append(&sql, 0, " %s", ast_str_buffer(where)); - ast_debug(1, "SQL query: %s\n", ast_str_buffer(sql)); - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, ast_str_buffer(sql), NULL, NULL, &errormsg); - RES_CONFIG_SQLITE_END(error) - - if (!error) { - rows_num = sqlite_changes(db); - } else { - rows_num = -1; - } - - ast_mutex_unlock(&mutex); - - if (error) { - ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - } - sqlite_freemem(errormsg); - - return rows_num; -} - -static int realtime_store_handler(const char *database, const char *table, const struct ast_variable *fields) -{ - char *errormsg = NULL, *tmp_str, *tmp_keys = NULL, *tmp_keys2 = NULL, *tmp_vals = NULL, *tmp_vals2 = NULL; - const struct ast_variable *field = fields; - int error, rows_id; - - if (!table) { - ast_log(LOG_WARNING, "Table name unspecified\n"); - return -1; - } - - if (!fields) { - return -1; - } - -/* \cond DOXYGEN_CAN_PARSE_THIS */ -#undef QUERY -#define QUERY "INSERT into '%q' (%s) VALUES (%s);" -/* \endcond */ - - do { - if ( tmp_keys2 ) { - tmp_keys = sqlite_mprintf("%s, %q", tmp_keys2, field->name); - sqlite_freemem(tmp_keys2); - } else { - tmp_keys = sqlite_mprintf("%q", field->name); - } - if (!tmp_keys) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - sqlite_freemem(tmp_vals); - return -1; - } - - if ( tmp_vals2 ) { - tmp_vals = sqlite_mprintf("%s, '%q'", tmp_vals2, field->value); - sqlite_freemem(tmp_vals2); - } else { - tmp_vals = sqlite_mprintf("'%q'", field->value); - } - if (!tmp_vals) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - sqlite_freemem(tmp_keys); - return -1; - } - - - tmp_keys2 = tmp_keys; - tmp_vals2 = tmp_vals; - } while ((field = field->next)); - - if (!(tmp_str = sqlite_mprintf(QUERY, table, tmp_keys, tmp_vals))) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - sqlite_freemem(tmp_keys); - sqlite_freemem(tmp_vals); - return -1; - } - - sqlite_freemem(tmp_keys); - sqlite_freemem(tmp_vals); - - ast_debug(1, "SQL query: %s\n", tmp_str); - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, tmp_str, NULL, NULL, &errormsg); - RES_CONFIG_SQLITE_END(error) - - if (!error) { - rows_id = sqlite_last_insert_rowid(db); - } else { - rows_id = -1; - } - - ast_mutex_unlock(&mutex); - - sqlite_freemem(tmp_str); - - if (error) { - ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - } - sqlite_freemem(errormsg); - - return rows_id; -} - -static int realtime_destroy_handler(const char *database, const char *table, - const char *keyfield, const char *entity, const struct ast_variable *fields) -{ - char *query, *errormsg = NULL, *tmp_str; - const struct ast_variable *field; - int error, rows_num; - - if (!table) { - ast_log(LOG_WARNING, "Table name unspecified\n"); - return -1; - } - -/* \cond DOXYGEN_CAN_PARSE_THIS */ -#undef QUERY -#define QUERY "DELETE FROM '%q' WHERE" -/* \endcond */ - - if (!(query = sqlite_mprintf(QUERY, table))) { - ast_log(LOG_WARNING, "Unable to allocate SQL query\n"); - return -1; - } - - for (field = fields; field; field = field->next) { - tmp_str = sqlite_mprintf("%s %q = '%q' AND", query, field->name, field->value); - sqlite_freemem(query); - - if (!tmp_str) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - return -1; - } - - query = tmp_str; - } - - if (!(tmp_str = sqlite_mprintf("%s %q = '%q';", query, keyfield, entity))) { - ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); - sqlite_freemem(query); - return -1; - } - sqlite_freemem(query); - query = tmp_str; - ast_debug(1, "SQL query: %s\n", query); - - ast_mutex_lock(&mutex); - - RES_CONFIG_SQLITE_BEGIN - error = sqlite_exec(db, query, NULL, NULL, &errormsg); - RES_CONFIG_SQLITE_END(error) - - if (!error) { - rows_num = sqlite_changes(db); - } else { - rows_num = -1; - } - - ast_mutex_unlock(&mutex); - - sqlite_freemem(query); - - if (error) { - ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - } - sqlite_freemem(errormsg); - - return rows_num; -} - -static int realtime_require_handler(const char *unused, const char *tablename, va_list ap) -{ - struct sqlite_cache_tables *tbl = find_table(tablename); - struct sqlite_cache_columns *col; - char *elm; - int type, res = 0; - - if (!tbl) { - return -1; - } - - while ((elm = va_arg(ap, char *))) { - type = va_arg(ap, require_type); - va_arg(ap, int); - /* Check if the field matches the criteria */ - AST_RWLIST_TRAVERSE(&tbl->columns, col, list) { - if (strcmp(col->name, elm) == 0) { - /* SQLite only has two types - the 32-bit integer field that - * is the key column, and everything else (everything else - * being a string). - */ - if (col->isint && !ast_rq_is_int(type)) { - ast_log(LOG_WARNING, "Realtime table %s: column '%s' is an integer field, but Asterisk requires that it not be!\n", tablename, col->name); - res = -1; - } - break; - } - } - if (!col) { - ast_log(LOG_WARNING, "Realtime table %s requires column '%s', but that column does not exist!\n", tablename, elm); - } - } - AST_RWLIST_UNLOCK(&(tbl->columns)); - return res; -} - -static int realtime_unload_handler(const char *unused, const char *tablename) -{ - struct sqlite_cache_tables *tbl; - AST_RWLIST_WRLOCK(&sqlite_tables); - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sqlite_tables, tbl, list) { - if (!strcasecmp(tbl->name, tablename)) { - AST_RWLIST_REMOVE_CURRENT(list); - free_table(tbl); - } - } - AST_RWLIST_TRAVERSE_SAFE_END - AST_RWLIST_UNLOCK(&sqlite_tables); - return 0; -} - -static char *handle_cli_show_sqlite_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - switch (cmd) { - case CLI_INIT: - e->command = "sqlite show status"; - e->usage = - "Usage: sqlite show status\n" - " Show status information about the SQLite 2 driver\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != 3) - return CLI_SHOWUSAGE; - - ast_cli(a->fd, "SQLite database path: %s\n", dbfile); - ast_cli(a->fd, "config_table: "); - - if (!config_table) - ast_cli(a->fd, "unspecified, must be present in extconfig.conf\n"); - else - ast_cli(a->fd, "%s\n", config_table); - - ast_cli(a->fd, "cdr_table: "); - - if (!cdr_table) - ast_cli(a->fd, "unspecified, CDR support disabled\n"); - else - ast_cli(a->fd, "%s\n", cdr_table); - - return CLI_SUCCESS; -} - -static char *handle_cli_sqlite_show_tables(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - struct sqlite_cache_tables *tbl; - struct sqlite_cache_columns *col; - int found = 0; - - switch (cmd) { - case CLI_INIT: - e->command = "sqlite show tables"; - e->usage = - "Usage: sqlite show tables\n" - " Show table information about the SQLite 2 driver\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != 3) - return CLI_SHOWUSAGE; - - AST_RWLIST_RDLOCK(&sqlite_tables); - AST_RWLIST_TRAVERSE(&sqlite_tables, tbl, list) { - found++; - ast_cli(a->fd, "Table %s:\n", tbl->name); - AST_RWLIST_TRAVERSE(&(tbl->columns), col, list) { - fprintf(stderr, "%s\n", col->name); - ast_cli(a->fd, " %20.20s %-30.30s\n", col->name, col->type); - } - } - AST_RWLIST_UNLOCK(&sqlite_tables); - - if (!found) { - ast_cli(a->fd, "No tables currently in cache\n"); - } - - return CLI_SUCCESS; -} - -static int unload_module(void) -{ - if (cdr_registered && ast_cdr_unregister(RES_CONFIG_SQLITE_NAME)) { - return -1; - } - - if (cli_status_registered) { - ast_cli_unregister_multiple(cli_status, ARRAY_LEN(cli_status)); - } - - ast_config_engine_deregister(&sqlite_engine); - - if (db) - sqlite_close(db); - - unload_config(); - - return 0; -} - -/*! - * \brief Load the module - * - * Module loading including tests for configuration or dependencies. - * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, - * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails - * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the - * configuration file or other non-critical problem return - * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. - */ -static int load_module(void) -{ - char *errormsg = NULL; - int error; - - db = NULL; - cdr_registered = 0; - cli_status_registered = 0; - dbfile = NULL; - config_table = NULL; - cdr_table = NULL; - error = load_config(); - - if (error) - return AST_MODULE_LOAD_DECLINE; - - if (!(db = sqlite_open(dbfile, 0660, &errormsg))) { - ast_log(LOG_ERROR, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - unload_module(); - return AST_MODULE_LOAD_DECLINE; - } - - sqlite_freemem(errormsg); - errormsg = NULL; - ast_config_engine_register(&sqlite_engine); - - if (use_cdr) { - char *query; - -/* \cond DOXYGEN_CAN_PARSE_THIS */ -#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 AST_MODULE_LOAD_DECLINE; - } - - 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) { - /* - * Unexpected error. - */ - if (error != SQLITE_ERROR) { - ast_log(LOG_ERROR, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - unload_module(); - return AST_MODULE_LOAD_DECLINE; - } - - sqlite_freemem(errormsg); - errormsg = NULL; - query = sqlite_mprintf(sql_create_cdr_table, cdr_table); - - if (!query) { - ast_log(LOG_ERROR, "Unable to allocate SQL query\n"); - unload_module(); - return AST_MODULE_LOAD_DECLINE; - } - - 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) { - ast_log(LOG_ERROR, "%s\n", S_OR(errormsg, sqlite_error_string(error))); - sqlite_freemem(errormsg); - unload_module(); - return AST_MODULE_LOAD_DECLINE; - } - } - sqlite_freemem(errormsg); - errormsg = NULL; - - error = ast_cdr_register(RES_CONFIG_SQLITE_NAME, RES_CONFIG_SQLITE_DESCRIPTION, cdr_handler); - - if (error) { - unload_module(); - return AST_MODULE_LOAD_DECLINE; - } - - cdr_registered = 1; - } - - error = ast_cli_register_multiple(cli_status, ARRAY_LEN(cli_status)); - - if (error) { - unload_module(); - return AST_MODULE_LOAD_DECLINE; - } - - cli_status_registered = 1; - - return AST_MODULE_LOAD_SUCCESS; -} - -/* - * This module should require "cdr" to enforce startup/shutdown ordering but it - * loads at REALTIME_DRIVER priority which would cause "cdr" to load too early. - * - * ast_cdr_register / ast_cdr_unregister is safe for use while "cdr" is not running. - */ -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime SQLite configuration", - .support_level = AST_MODULE_SUPPORT_DEPRECATED, - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_REALTIME_DRIVER, - .requires = "extconfig", -);