diff --git a/cdr/cdr_sqlite3.c b/cdr/cdr_sqlite3.c new file mode 100644 index 0000000..9e28954 --- /dev/null +++ b/cdr/cdr_sqlite3.c @@ -0,0 +1,229 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006, Moises Silva + * + * + * Based on original cdr_sqlite.c file by Holger Schurig + * + * 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. + */ + +/*! \file + * + * \brief Store CDR records in a SQLite3 database. + * + * \author Moises Silva + * + * See also + * \arg \ref Config_cdr + * \arg http://www.sqlite.org/ + * + * Creates the database and table on-the-fly + * \ingroup cdr_drivers + */ + +/*** MODULEINFO + sqlite3 + ***/ + +#include "asterisk.h" + +#include + +#include +#include +#include +#include +#include + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: ?? $") + +#include "asterisk/channel.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" + +#define LOG_UNIQUEID 1 +#define LOG_USERFIELD 1 + +/* When you change the DATE_FORMAT, be sure to change the CHAR(19) below to something else */ +#define DATE_FORMAT "%Y-%m-%d %T" + +static char *desc = "SQLite3 CDR Backend"; +static char *name = "sqlite3"; +static sqlite3* sqlite3_db = NULL; + +AST_MUTEX_DEFINE_STATIC(sqlite3_lock); + +/*! \brief SQL table format */ +static char sql_create_table[] = "CREATE TABLE cdr (" +" AcctId INTEGER PRIMARY KEY," +" clid VARCHAR(80)," +" src VARCHAR(80)," +" dst VARCHAR(80)," +" dcontext VARCHAR(80)," +" channel VARCHAR(80)," +" dstchannel VARCHAR(80)," +" lastapp VARCHAR(80)," +" lastdata VARCHAR(80)," +" start CHAR(19)," +" answer CHAR(19)," +" end CHAR(19)," +" duration INTEGER," +" billsec INTEGER," +" disposition INTEGER," +" amaflags INTEGER," +" accountcode VARCHAR(20)" +#if LOG_UNIQUEID +" ,uniqueid VARCHAR(32)" +#endif +#if LOG_USERFIELD +" ,userfield VARCHAR(255)" +#endif +");"; + +static int sqlite_log(struct ast_cdr *cdr) +{ + int result = 0; + char *query_error; + struct tm tm; + time_t t; + char startstr[80], answerstr[80], endstr[80]; + int count; + + ast_mutex_lock(&sqlite3_lock); + + t = cdr->start.tv_sec; + localtime_r(&t, &tm); + strftime(startstr, sizeof(startstr), DATE_FORMAT, &tm); + + t = cdr->answer.tv_sec; + localtime_r(&t, &tm); + strftime(answerstr, sizeof(answerstr), DATE_FORMAT, &tm); + + t = cdr->end.tv_sec; + localtime_r(&t, &tm); + strftime(endstr, sizeof(endstr), DATE_FORMAT, &tm); + char *sql_query = sqlite3_mprintf( + "INSERT INTO cdr (" + "clid,src,dst,dcontext," + "channel,dstchannel,lastapp,lastdata, " + "start,answer,end," + "duration,billsec,disposition,amaflags, " + "accountcode" +# if LOG_UNIQUEID + ",uniqueid" +# endif +# if LOG_USERFIELD + ",userfield" +# endif + ") VALUES (" + "'%q', '%q', '%q', '%q', " + "'%q', '%q', '%q', '%q', " + "'%q', '%q', '%q', " + "%d, %d, %d, %d, " + "'%q'" +# if LOG_UNIQUEID + ",'%q'" +# endif +# if LOG_USERFIELD + ",'%q'" +# endif + ")", cdr->clid, cdr->src, cdr->dst, cdr->dcontext, + cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, + startstr, answerstr, endstr, + cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags, + cdr->accountcode +# if LOG_UNIQUEID + ,cdr->uniqueid +# endif +# if LOG_USERFIELD + ,cdr->userfield +# endif + ); + for ( count = 0; count < 5; count++ ) { + result = sqlite3_exec(sqlite3_db, sql_query, NULL, NULL, &query_error); + /* if not busy or locked, then end here, otherwise try again in 200 us */ + if ( SQLITE_BUSY != result && SQLITE_LOCKED != result ) { + break; + } + usleep(200); + } + + if ( query_error ) { + ast_log(LOG_ERROR, "cdr_sqlite: %s\n", query_error); + sqlite3_free(query_error); + } + sqlite3_free(sql_query); + ast_mutex_unlock(&sqlite3_lock); + return result; +} + +char *description(void) +{ + return desc; +} + +int unload_module(void) +{ + if (sqlite3_db) + sqlite3_close(sqlite3_db); + ast_cdr_unregister(name); + return 0; +} + +static int load_module(void *mod) +{ + char database_file[PATH_MAX]; + int result; + + /* is the database there? */ + snprintf(database_file, sizeof(database_file), "%s/sqlite3_cdr.db", ast_config_AST_LOG_DIR); + + result = sqlite3_open(database_file, &sqlite3_db); + + if ( SQLITE_OK != result ) { + const char *error_message = sqlite3_errmsg(sqlite3_db); + int error_number = sqlite3_errcode(sqlite3_db); + ast_log(LOG_ERROR, "cdr_sqlite3: [errno %d] %s\n", error_number, error_message); + /* according to comments in sqlite3.h sqlite3_open always return a valid handler to close */ + sqlite3_close(sqlite3_db); + return -1; + } + + /* is the table there? */ + result = sqlite3_exec(sqlite3_db, "SELECT COUNT(AcctId) FROM cdr;", NULL, NULL, NULL); + if ( SQLITE_OK != result ) { + char *query_error; + result = sqlite3_exec(sqlite3_db, sql_create_table, NULL, NULL, &query_error); + if ( SQLITE_OK != result ) { + ast_log(LOG_ERROR, "cdr_sqlite3: Unable to create table 'cdr': %s\n", query_error); + sqlite3_free(query_error); + sqlite3_close(sqlite3_db); + return AST_MODULE_LOAD_FAILURE; + } + + /* TODO: here we should probably create an index */ + } + + result = ast_cdr_register(name, desc, sqlite_log); + if ( result ) { + ast_log(LOG_ERROR, "Unable to register SQLite3 CDR handling\n"); + return AST_MODULE_LOAD_FAILURE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SQLite3 CDR Backend"); + diff --git a/configure.ac b/configure.ac index 0cc4ac5..82b400d 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,7 @@ AST_EXT_LIB_SETUP([QT], [Qt], [qt]) AST_EXT_LIB_SETUP([RADIUS], [Radius Client], [radius]) AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex]) AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite]) +AST_EXT_LIB_CHECK([SQLITE3], [sqlite3], [sqlite3_exec], [sqlite3.h], [-lsqlite3]) AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv]) AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL], [ssl]) AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds]) diff --git a/makeopts.in b/makeopts.in index bf9d65a..533637b 100644 --- a/makeopts.in +++ b/makeopts.in @@ -132,6 +132,9 @@ SPEEX_LIB=@SPEEX_LIB@ SQLITE_INCLUDE=@SQLITE_INCLUDE@ SQLITE_LIB=@SQLITE_LIB@ +SQLITE3_LIB=@SQLITE3_LIB@ +SQLITE3_INCLUDE=@SQLITE3_INCLUDE@ + SSL_INCLUDE=@OPENSSL_INCLUDE@ SSL_LIB=@OPENSSL_LIB@