mirror of https://github.com/asterisk/asterisk
* Add new module, cdr_sqlite3_custom which allows logging custom CDRs into a SQLite3 database. (issue #7149, alerios) * Add new module, res_config_sqlite, which adds realtime database configuration support for SQLite version 2. I decided that this was ok since we didn't have any realtime support for version 3. If someone ports this to version 3, then version 2 support can be removed or marked deprecated. (issue #7790, rbarun_proformatique) * Mark cdr_sqlite as deprecated in favor of cdr_sqlite3_custom. Also, note that there were other modules on the bug tracker that did not make the cut because they provided some duplicated functionality. Those are: * cdr_sqlite3 (issue #6754, moy) * cdr_sqlite3 (issue #8694, bsd) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@58866 65c4cc65-6c06-0410-ace0-fbb531ad65f31.6.0
parent
b4490af8ac
commit
5bea998a55
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2007, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com> and others.
|
||||
*
|
||||
* 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 Custom SQLite3 CDR records.
|
||||
*
|
||||
* \author Adapted by Alejandro Rios <alejandro.rios@avatar.com.co> and
|
||||
* Russell Bryant <russell@digium.com> from
|
||||
* cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>,
|
||||
* and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de>
|
||||
*
|
||||
*
|
||||
* \arg See also \ref AstCDR
|
||||
*
|
||||
*
|
||||
* \ingroup cdr_drivers
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>sqlite3</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/cdr.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(lock);
|
||||
|
||||
static const char config_file[] = "cdr_sqlite3_custom.conf";
|
||||
|
||||
static char *desc = "Customizable SQLite3 CDR Backend";
|
||||
static char *name = "cdr_sqlite3_custom";
|
||||
static sqlite3 *db = NULL;
|
||||
|
||||
static char table[80];
|
||||
static char columns[1024];
|
||||
static char values[1024];
|
||||
|
||||
static int load_config(int reload)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_variable *mappingvar;
|
||||
const char *tmp;
|
||||
|
||||
if (!(cfg = ast_config_load(config_file))) {
|
||||
if (reload)
|
||||
ast_log(LOG_WARNING, "%s: Failed to reload configuration file.\n", name);
|
||||
else {
|
||||
ast_log(LOG_WARNING,
|
||||
"%s: Failed to load configuration file. Module not activated.\n",
|
||||
name);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!reload)
|
||||
ast_mutex_lock(&lock);
|
||||
|
||||
if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
|
||||
/* nothing configured */
|
||||
ast_config_destroy(cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mapping must have a table name */
|
||||
tmp = ast_variable_retrieve(cfg, "master", "table");
|
||||
if (!ast_strlen_zero(tmp))
|
||||
ast_copy_string(table, tmp, sizeof(table));
|
||||
else {
|
||||
ast_log(LOG_WARNING, "%s: Table name not specified. Assuming cdr.\n", name);
|
||||
strcpy(table, "cdr");
|
||||
}
|
||||
|
||||
tmp = ast_variable_retrieve(cfg, "master", "columns");
|
||||
if (!ast_strlen_zero(tmp))
|
||||
ast_copy_string(columns, tmp, sizeof(columns));
|
||||
else {
|
||||
ast_log(LOG_WARNING, "%s: Column names not specified. Module not loaded.\n",
|
||||
name);
|
||||
ast_config_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp = ast_variable_retrieve(cfg, "master", "values");
|
||||
if (!ast_strlen_zero(tmp))
|
||||
ast_copy_string(values, tmp, sizeof(values));
|
||||
else {
|
||||
ast_log(LOG_WARNING, "%s: Values not specified. Module not loaded.\n", name);
|
||||
ast_config_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!reload)
|
||||
ast_mutex_unlock(&lock);
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* assumues 'to' buffer is at least strlen(from) * 2 + 1 bytes */
|
||||
static int do_escape(char *to, const char *from)
|
||||
{
|
||||
char *out = to;
|
||||
|
||||
for (; *from; from++) {
|
||||
if (*from == '\'' || *from == '\\')
|
||||
*out++ = *from;
|
||||
*out++ = *from;
|
||||
}
|
||||
*out = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sqlite3_log(struct ast_cdr *cdr)
|
||||
{
|
||||
int res = 0;
|
||||
char *zErr = 0;
|
||||
char *sql_cmd;
|
||||
struct ast_channel dummy = { 0, };
|
||||
int count;
|
||||
|
||||
{ /* Make it obvious that only sql_cmd should be used outside of this block */
|
||||
char *sql_tmp_cmd;
|
||||
char sql_insert_cmd[2048] = "";
|
||||
sql_tmp_cmd = sqlite3_mprintf("INSERT INTO %q (%q) VALUES (%q)", table, columns, values);
|
||||
dummy.cdr = cdr;
|
||||
pbx_substitute_variables_helper(&dummy, sql_tmp_cmd, sql_insert_cmd, sizeof(sql_insert_cmd) - 1);
|
||||
sqlite3_free(sql_tmp_cmd);
|
||||
sql_cmd = alloca(strlen(sql_insert_cmd) * 2 + 1);
|
||||
do_escape(sql_cmd, sql_insert_cmd);
|
||||
}
|
||||
|
||||
ast_mutex_lock(&lock);
|
||||
|
||||
for (count = 0; count < 5; count++) {
|
||||
res = sqlite3_exec(db, sql_cmd, NULL, NULL, &zErr);
|
||||
if (res != SQLITE_BUSY && res != SQLITE_LOCKED)
|
||||
break;
|
||||
usleep(200);
|
||||
}
|
||||
|
||||
if (zErr) {
|
||||
ast_log(LOG_ERROR, "%s: %s. sentence: %s.\n", name, zErr, sql_cmd);
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
if (db)
|
||||
sqlite3_close(db);
|
||||
|
||||
ast_cdr_unregister(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
char *zErr;
|
||||
char fn[PATH_MAX];
|
||||
int res;
|
||||
char *sql_cmd;
|
||||
|
||||
if (!load_config(0)) {
|
||||
res = ast_cdr_register(name, desc, sqlite3_log);
|
||||
if (res) {
|
||||
ast_log(LOG_ERROR, "%s: Unable to register custom SQLite3 CDR handling\n", name);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
}
|
||||
|
||||
/* is the database there? */
|
||||
snprintf(fn, sizeof(fn), "%s/master.db", ast_config_AST_LOG_DIR);
|
||||
res = sqlite3_open(fn, &db);
|
||||
if (!db) {
|
||||
ast_log(LOG_ERROR, "%s: Could not open database %s.\n", name, fn);
|
||||
sqlite3_free(zErr);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
/* is the table there? */
|
||||
sql_cmd = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
|
||||
res = sqlite3_exec(db, sql_cmd, NULL, NULL, NULL);
|
||||
sqlite3_free(sql_cmd);
|
||||
if (res) {
|
||||
sql_cmd = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY,%q)", table, columns);
|
||||
res = sqlite3_exec(db, sql_cmd, NULL, NULL, &zErr);
|
||||
sqlite3_free(sql_cmd);
|
||||
if (zErr) {
|
||||
ast_log(LOG_WARNING, "%s: %s.\n", name, zErr);
|
||||
sqlite3_free(zErr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (res) {
|
||||
ast_log(LOG_ERROR, "%s: Unable to create table '%s': %s.\n", name, table, zErr);
|
||||
sqlite3_free(zErr);
|
||||
if (db)
|
||||
sqlite3_close(db);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reload(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_mutex_lock(&lock);
|
||||
res = load_config(1);
|
||||
ast_mutex_unlock(&lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CDR Module",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
);
|
@ -0,0 +1,7 @@
|
||||
;
|
||||
; Mappings for custom config file
|
||||
;
|
||||
[master] ; currently, only file "master.db" is supported, with only one table at a time.
|
||||
table => cdr
|
||||
columns => calldate, clid, dcontext, channel, dstchannel, lastapp, lastdata, duration, billsec, disposition, amaflags, accountcode, uniqueid, userfield, test
|
||||
values => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration)}','${CDR(billsec)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'
|
@ -0,0 +1,15 @@
|
||||
[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
|
||||
|
||||
; This parameter controls the registration of the SQLITE() Dialplan application.
|
||||
app_enable => yes
|
||||
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* res_sqlite - SQLite 2 support for Asterisk
|
||||
*
|
||||
* 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
|
||||
* module, and the configs/ directory for the sample configuration file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tables for res_config_sqlite.so.
|
||||
*/
|
||||
|
||||
/*
|
||||
* RealTime static table.
|
||||
*/
|
||||
CREATE TABLE ast_config
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
commented INT(11) NOT NULL DEFAULT '0',
|
||||
filename VARCHAR(128) NOT NULL,
|
||||
category VARCHAR(128) NOT NULL,
|
||||
var_name VARCHAR(128) NOT NULL,
|
||||
var_val VARCHAR(128) NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX ast_config_filename_commented ON ast_config(filename, commented);
|
||||
|
||||
/*
|
||||
* 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 ''
|
||||
* );
|
||||
*/
|
||||
|
||||
/*
|
||||
* SIP RealTime table.
|
||||
*/
|
||||
CREATE TABLE ast_sip
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
commented INT(11) NOT NULL DEFAULT '0',
|
||||
name VARCHAR(80) NOT NULL,
|
||||
accountcode VARCHAR(20),
|
||||
amaflags VARCHAR(13),
|
||||
callgroup VARCHAR(10),
|
||||
callerid VARCHAR(80),
|
||||
canreinvite CHAR(3),
|
||||
context VARCHAR(80),
|
||||
defaultip VARCHAR(15),
|
||||
dtmfmode VARCHAR(7),
|
||||
fromuser VARCHAR(80),
|
||||
fromdomain VARCHAR(80),
|
||||
fullcontact VARCHAR(80),
|
||||
host VARCHAR(31) NOT NULL,
|
||||
insecure VARCHAR(4),
|
||||
language CHAR(2),
|
||||
mailbox VARCHAR(50),
|
||||
md5secret VARCHAR(80),
|
||||
nat VARCHAR(5) NOT NULL DEFAULT 'no',
|
||||
deny VARCHAR(95),
|
||||
permit VARCHAR(95),
|
||||
mask VARCHAR(95),
|
||||
pickupgroup VARCHAR(10),
|
||||
port VARCHAR(5) NOT NULL,
|
||||
qualify CHAR(3),
|
||||
restrictcid CHAR(1),
|
||||
rtptimeout CHAR(3),
|
||||
rtpholdtimeout CHAR(3),
|
||||
secret VARCHAR(80),
|
||||
type VARCHAR(6) NOT NULL DEFAULT 'friend',
|
||||
username VARCHAR(80) NOT NULL,
|
||||
disallow VARCHAR(100),
|
||||
allow VARCHAR(100),
|
||||
musiconhold VARCHAR(100),
|
||||
regseconds INT(11) NOT NULL DEFAULT '0',
|
||||
ipaddr VARCHAR(15) NOT NULL,
|
||||
regexten VARCHAR(80) NOT NULL,
|
||||
cancallforward CHAR(3),
|
||||
setvar VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX ast_sip_name ON ast_sip(name);
|
||||
|
||||
/*
|
||||
* Dialplan RealTime table.
|
||||
*/
|
||||
CREATE TABLE ast_exten
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
commented INT(11) NOT NULL DEFAULT '0',
|
||||
context VARCHAR(20) NOT NULL,
|
||||
exten VARCHAR(20) NOT NULL,
|
||||
priority TINYINT(4) NOT NULL,
|
||||
app VARCHAR(20) NOT NULL,
|
||||
appdata VARCHAR(128) NOT NULL
|
||||
);
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue