dect
/
asterisk
Archived
13
0
Fork 0

Merge in changes from my cdr-tds-conversion branch. This changes the internal

implementation from using the volatile libtds, to using the db-lib front end.
The unintended side effect of this is that we support (at least) versions 0.62
through 0.82 of the FreeTDS distribution without any #ifdef ugliness.

(closes issue #12844)
Reported by: jcollie


git-svn-id: http://svn.digium.com/svn/asterisk/trunk@126226 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
seanbright 2008-06-28 21:28:16 +00:00
parent 7fadd4049b
commit 13e31ad1ef
5 changed files with 9369 additions and 2941 deletions

View File

@ -155,6 +155,9 @@ CDR:
possible values the systemname prefix could be. In the past, if the possible values the systemname prefix could be. In the past, if the
systemname was too long, the uniqueid would have been truncated. systemname was too long, the uniqueid would have been truncated.
* The cdr_tds module now supports all versions of FreeTDS that contain
the db-lib frontend.
Formats: Formats:
* format_wav: The GAIN preprocessor definition and source code that used it * format_wav: The GAIN preprocessor definition and source code that used it

View File

@ -66,82 +66,76 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#include <tds.h>
#include <tdsconvert.h>
#include <ctype.h>
#include "asterisk/config.h" #include "asterisk/config.h"
#include "asterisk/channel.h" #include "asterisk/channel.h"
#include "asterisk/cdr.h" #include "asterisk/cdr.h"
#include "asterisk/module.h" #include "asterisk/module.h"
#ifdef FREETDS_PRE_0_62 #include <sqlfront.h>
#warning "You have older TDS, you should upgrade!" #include <sybdb.h>
#endif
#define DATE_FORMAT "%Y/%m/%d %T" #define DATE_FORMAT "%Y/%m/%d %T"
static char *name = "mssql"; static char *name = "FreeTDS (MSSQL)";
static char *config = "cdr_tds.conf"; static char *config = "cdr_tds.conf";
struct cdr_tds_config { struct cdr_tds_config {
AST_DECLARE_STRING_FIELDS( AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(hostname); AST_STRING_FIELD(hostname);
AST_STRING_FIELD(dbname); AST_STRING_FIELD(database);
AST_STRING_FIELD(dbuser); AST_STRING_FIELD(username);
AST_STRING_FIELD(password); AST_STRING_FIELD(password);
AST_STRING_FIELD(table); AST_STRING_FIELD(table);
AST_STRING_FIELD(charset); AST_STRING_FIELD(charset);
AST_STRING_FIELD(language); AST_STRING_FIELD(language);
); );
TDSSOCKET *tds; DBPROCESS *dbproc;
TDSLOGIN *login;
TDSCONTEXT *context;
unsigned int connected:1; unsigned int connected:1;
ast_mutex_t lock;
}; };
AST_MUTEX_DEFINE_STATIC(tds_lock);
static struct cdr_tds_config *settings; static struct cdr_tds_config *settings;
static char *anti_injection(const char *, int); static char *anti_injection(const char *, int);
static void get_date(char *, struct timeval); static void get_date(char *, size_t len, struct timeval);
static int mssql_connect(void); static int mssql_connect(void);
static int mssql_disconnect(void); static int mssql_disconnect(void);
static void cdr_tds_config_destroy(void);
static int tds_log(struct ast_cdr *cdr) static int tds_log(struct ast_cdr *cdr)
{ {
char sqlcmd[2048], start[80], answer[80], end[80]; char start[80], answer[80], end[80];
char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid; char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid;
int res = 0; RETCODE erc;
int retried = 0; int res = -1;
#ifdef FREETDS_PRE_0_62
TDS_INT result_type;
#endif
ast_mutex_lock(&settings->lock);
memset(sqlcmd, 0, 2048);
accountcode = anti_injection(cdr->accountcode, 20); accountcode = anti_injection(cdr->accountcode, 20);
src = anti_injection(cdr->src, 80); src = anti_injection(cdr->src, 80);
dst = anti_injection(cdr->dst, 80); dst = anti_injection(cdr->dst, 80);
dcontext = anti_injection(cdr->dcontext, 80); dcontext = anti_injection(cdr->dcontext, 80);
clid = anti_injection(cdr->clid, 80); clid = anti_injection(cdr->clid, 80);
channel = anti_injection(cdr->channel, 80); channel = anti_injection(cdr->channel, 80);
dstchannel = anti_injection(cdr->dstchannel, 80); dstchannel = anti_injection(cdr->dstchannel, 80);
lastapp = anti_injection(cdr->lastapp, 80); lastapp = anti_injection(cdr->lastapp, 80);
lastdata = anti_injection(cdr->lastdata, 80); lastdata = anti_injection(cdr->lastdata, 80);
uniqueid = anti_injection(cdr->uniqueid, 32); uniqueid = anti_injection(cdr->uniqueid, 32);
get_date(start, cdr->start); get_date(start, sizeof(start), cdr->start);
get_date(answer, cdr->answer); get_date(answer, sizeof(answer), cdr->answer);
get_date(end, cdr->end); get_date(end, sizeof(end), cdr->end);
sprintf( ast_mutex_lock(&tds_lock);
sqlcmd,
/* Ensure that we are connected */
if (!settings->connected) {
if (mssql_connect()) {
/* Connect failed */
goto done;
}
}
erc = dbfcmd(settings->dbproc,
"INSERT INTO %s " "INSERT INTO %s "
"(" "("
"accountcode, " "accountcode, "
@ -202,27 +196,26 @@ static int tds_log(struct ast_cdr *cdr)
uniqueid uniqueid
); );
do { if (erc == FAIL) {
if (!settings->connected) { ast_log(LOG_ERROR, "Failed to build INSERT statement, no CDR was logged.\n");
if (mssql_connect()) goto done;
ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n"); }
else
ast_log(LOG_WARNING, "Reconnected to SQL database.\n");
retried = 1; /* note that we have now tried */ if (dbsqlexec(settings->dbproc) == FAIL) {
} ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CDR was logged.\n");
goto done;
}
#ifdef FREETDS_PRE_0_62 /* Consume any results we might get back (this is more of a sanity check than
if (!settings->connected || (tds_submit_query(settings->tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(settings->tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED)) * anything else, since an INSERT shouldn't return results). */
#else while (dbresults(settings->dbproc) != NO_MORE_RESULTS) {
if (!settings->connected || (tds_submit_query(settings->tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(settings->tds) != TDS_SUCCEED)) while (dbnextrow(settings->dbproc) != NO_MORE_ROWS);
#endif }
{
ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
mssql_disconnect(); /* this is ok even if we are already disconnected */ res = 0;
}
} while (!settings->connected && !retried); done:
ast_mutex_unlock(&tds_lock);
ast_free(accountcode); ast_free(accountcode);
ast_free(src); ast_free(src);
@ -235,9 +228,7 @@ static int tds_log(struct ast_cdr *cdr)
ast_free(lastdata); ast_free(lastdata);
ast_free(uniqueid); ast_free(uniqueid);
ast_mutex_unlock(&settings->lock); return 0;
return res;
} }
static char *anti_injection(const char *str, int len) static char *anti_injection(const char *str, int len)
@ -274,39 +265,23 @@ static char *anti_injection(const char *str, int len)
return buf; return buf;
} }
static void get_date(char *dateField, struct timeval tv) static void get_date(char *dateField, size_t len, struct timeval tv)
{ {
struct ast_tm tm;
char buf[80];
/* To make sure we have date variable if not insert null to SQL */ /* To make sure we have date variable if not insert null to SQL */
if (!ast_tvzero(tv)) { if (!ast_tvzero(tv)) {
struct ast_tm tm;
ast_localtime(&tv, &tm, NULL); ast_localtime(&tv, &tm, NULL);
ast_strftime(buf, 80, DATE_FORMAT, &tm); ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm);
sprintf(dateField, "'%s'", buf);
} else { } else {
strcpy(dateField, "null"); ast_copy_string(dateField, "null", len);
} }
} }
static int mssql_disconnect(void) static int mssql_disconnect(void)
{ {
if (!settings) if (settings->dbproc) {
return 0; dbclose(settings->dbproc);
settings->dbproc = NULL;
if (settings->tds) {
tds_free_socket(settings->tds);
settings->tds = NULL;
}
if (settings->context) {
tds_free_context(settings->context);
settings->context = NULL;
}
if (settings->login) {
tds_free_login(settings->login);
settings->login = NULL;
} }
settings->connected = 0; settings->connected = 0;
@ -316,107 +291,90 @@ static int mssql_disconnect(void)
static int mssql_connect(void) static int mssql_connect(void)
{ {
#if (defined(FREETDS_0_63) || defined(FREETDS_0_64)) LOGINREC *login;
TDSCONNECTION *connection = NULL;
#else
TDSCONNECTINFO *connection = NULL;
#endif
char query[128];
/* Connect to M$SQL Server */ if ((login = dblogin()) == NULL) {
if (!(settings->login = tds_alloc_login())) { ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n");
ast_log(LOG_ERROR, "tds_alloc_login() failed.\n");
return -1; return -1;
} }
tds_set_server(settings->login, settings->hostname); DBSETLAPP(login, "TSQL");
tds_set_user(settings->login, settings->dbuser); DBSETLUSER(login, settings->username);
tds_set_passwd(settings->login, settings->password); DBSETLPWD(login, settings->password);
tds_set_app(settings->login, "TSQL"); DBSETLCHARSET(login, settings->charset);
tds_set_library(settings->login, "TDS-Library"); DBSETLNATLANG(login, settings->language);
#ifndef FREETDS_PRE_0_62
tds_set_client_charset(settings->login, settings->charset);
#endif
tds_set_language(settings->login, settings->language);
tds_set_packet(settings->login, 512);
tds_set_version(settings->login, 7, 0);
#ifdef FREETDS_0_64 if ((settings->dbproc = dbopen(login, (char *) settings->hostname)) == NULL) {
if (!(settings->context = tds_alloc_context(NULL))) ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->hostname);
#else dbloginfree(login);
if (!(settings->context = tds_alloc_context())) return -1;
#endif
{
ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
goto connect_fail;
} }
if (!(settings->tds = tds_alloc_socket(settings->context, 512))) { dbloginfree(login);
ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
goto connect_fail; if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) {
ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database);
goto failed;
} }
tds_set_parent(settings->tds, NULL); if (dbfcmd(settings->dbproc, "SELECT 1 FROM [%s]", settings->table) == FAIL) {
connection = tds_read_config_info(settings->tds, settings->login, settings->context->locale); ast_log(LOG_ERROR, "Unable to build query while verifying the existence of table '%s'\n", settings->table);
if (!connection) { goto failed;
ast_log(LOG_ERROR, "tds_read_config() failed.\n");
goto connect_fail;
} }
if (tds_connect(settings->tds, connection) == TDS_FAIL) { if (dbsqlexec(settings->dbproc) == FAIL) {
ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n"); ast_log(LOG_ERROR, "Unable to verify existence of table '%s'\n", settings->table);
settings->tds = NULL; /* freed by tds_connect() on error */ goto failed;
#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
tds_free_connection(connection);
#else
tds_free_connect(connection);
#endif
connection = NULL;
goto connect_fail;
} }
#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
tds_free_connection(connection);
#else
tds_free_connect(connection);
#endif
connection = NULL;
sprintf(query, "USE %s", settings->dbname); /* Consume the result set (we don't really care about the result, though) */
#ifdef FREETDS_PRE_0_62 while (dbresults(settings->dbproc) != NO_MORE_RESULTS) {
if ((tds_submit_query(settings->tds, query) != TDS_SUCCEED) || (tds_process_simple_query(settings->tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED)) while (dbnextrow(settings->dbproc) != NO_MORE_ROWS);
#else
if ((tds_submit_query(settings->tds, query) != TDS_SUCCEED) || (tds_process_simple_query(settings->tds) != TDS_SUCCEED))
#endif
{
ast_log(LOG_ERROR, "Could not change database (%s)\n", settings->dbname);
goto connect_fail;
} }
settings->connected = 1; settings->connected = 1;
return 0; return 0;
connect_fail: failed:
mssql_disconnect(); dbclose(settings->dbproc);
settings->dbproc = NULL;
return -1; return -1;
} }
static void cdr_tds_config_destroy(void)
{
if (settings)
{
ast_mutex_destroy(&settings->lock);
ast_string_field_free_memory(settings);
ast_free(settings);
}
}
static int tds_unload_module(void) static int tds_unload_module(void)
{ {
mssql_disconnect(); if (settings) {
ast_mutex_lock(&tds_lock);
mssql_disconnect();
ast_mutex_unlock(&tds_lock);
ast_string_field_free_memory(settings);
ast_free(settings);
}
ast_cdr_unregister(name); ast_cdr_unregister(name);
cdr_tds_config_destroy(); dbexit();
return 0;
}
static int tds_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
{
ast_log(LOG_ERROR, "%s (%d)\n", dberrstr, dberr);
if (oserr != DBNOERR) {
ast_log(LOG_ERROR, "%s (%d)\n", oserrstr, oserr);
}
return INT_CANCEL;
}
static int tds_message_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
{
ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line);
ast_log(LOG_NOTICE, "%s\n", msgtext);
return 0; return 0;
} }
@ -424,66 +382,57 @@ static int tds_unload_module(void)
static int tds_load_module(int reload) static int tds_load_module(int reload)
{ {
struct ast_config *cfg; struct ast_config *cfg;
struct ast_variable *var;
const char *ptr = NULL; const char *ptr = NULL;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
cfg = ast_config_load(config, config_flags); cfg = ast_config_load(config, config_flags);
if (!cfg) { if (!cfg) {
ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDRs: %s\n", config); ast_log(LOG_NOTICE, "Unable to load TDS config for CDRs: %s\n", config);
return 0; return 0;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
return 0; return 0;
var = ast_variable_browse(cfg, "global"); if (!ast_variable_browse(cfg, "global")) {
if (!var) /* nothing configured */ { /* nothing configured */
ast_config_destroy(cfg); ast_config_destroy(cfg);
return 0; return 0;
} }
if (reload) { ast_mutex_lock(&tds_lock);
ast_string_field_init(settings, 0);
} else {
settings = ast_calloc(1, sizeof(*settings));
if (!settings || ast_string_field_init(settings, 256)) { /* Clear out any existing settings */
if (settings) { ast_string_field_init(settings, 0);
ast_free(settings);
settings = NULL;
}
ast_config_destroy(cfg);
return 0;
}
ast_mutex_init(&settings->lock);
}
ptr = ast_variable_retrieve(cfg, "global", "hostname"); ptr = ast_variable_retrieve(cfg, "global", "hostname");
if (ptr) { if (ptr) {
ast_string_field_set(settings, hostname, ptr); ast_string_field_set(settings, hostname, ptr);
} else { } else {
ast_log(LOG_ERROR, "Database server hostname not specified.\n"); ast_log(LOG_ERROR, "Failed to connect: Database server hostname not specified.\n");
goto failed;
} }
ptr = ast_variable_retrieve(cfg, "global", "dbname"); ptr = ast_variable_retrieve(cfg, "global", "dbname");
if (ptr) { if (ptr) {
ast_string_field_set(settings, dbname, ptr); ast_string_field_set(settings, database, ptr);
} else { } else {
ast_log(LOG_ERROR, "Database dbname not specified.\n"); ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n");
goto failed;
} }
ptr = ast_variable_retrieve(cfg, "global", "user"); ptr = ast_variable_retrieve(cfg, "global", "user");
if (ptr) { if (ptr) {
ast_string_field_set(settings, dbuser, ptr); ast_string_field_set(settings, username, ptr);
} else { } else {
ast_log(LOG_ERROR, "Database dbuser not specified.\n"); ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n");
goto failed;
} }
ptr = ast_variable_retrieve(cfg, "global", "password"); ptr = ast_variable_retrieve(cfg, "global", "password");
if (ptr) { if (ptr) {
ast_string_field_set(settings, password, ptr); ast_string_field_set(settings, password, ptr);
} else { } else {
ast_log(LOG_ERROR, "Database password not specified.\n"); ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n");
goto failed;
} }
ptr = ast_variable_retrieve(cfg, "global", "charset"); ptr = ast_variable_retrieve(cfg, "global", "charset");
@ -503,19 +452,28 @@ static int tds_load_module(int reload)
ptr = ast_variable_retrieve(cfg, "global", "table"); ptr = ast_variable_retrieve(cfg, "global", "table");
if (ptr) { if (ptr) {
ast_string_field_set(settings, table, ptr); ast_string_field_set(settings, table, ptr);
} else { } else {
ast_debug(1, "Table not specified. Assuming 'cdr'\n"); ast_log(LOG_NOTICE, "Table name not specified, using 'cdr' by default.\n");
ast_string_field_set(settings, table, "cdr"); ast_string_field_set(settings, table, "cdr");
} }
mssql_disconnect();
if (mssql_connect()) {
/* We failed to connect (mssql_connect takes care of logging it) */
goto failed;
}
ast_mutex_unlock(&tds_lock);
ast_config_destroy(cfg); ast_config_destroy(cfg);
ast_mutex_lock(&settings->lock);
mssql_disconnect();
mssql_connect();
ast_mutex_unlock(&settings->lock);
return 1; return 1;
failed:
ast_mutex_unlock(&tds_lock);
ast_config_destroy(cfg);
return 0;
} }
static int reload(void) static int reload(void)
@ -525,9 +483,35 @@ static int reload(void)
static int load_module(void) static int load_module(void)
{ {
if (!tds_load_module(0)) if (dbinit() == FAIL) {
ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n");
return AST_MODULE_LOAD_DECLINE; return AST_MODULE_LOAD_DECLINE;
}
dberrhandle(tds_error_handler);
dbmsghandle(tds_message_handler);
settings = ast_calloc(1, sizeof(*settings));
if (!settings || ast_string_field_init(settings, 256)) {
if (settings) {
ast_free(settings);
settings = NULL;
}
dbexit();
return AST_MODULE_LOAD_DECLINE;
}
if (!tds_load_module(0)) {
ast_string_field_free_memory(settings);
ast_free(settings);
settings = NULL;
dbexit();
return AST_MODULE_LOAD_DECLINE;
}
ast_cdr_register(name, ast_module_info->description, tds_log); ast_cdr_register(name, ast_module_info->description, tds_log);
return AST_MODULE_LOAD_SUCCESS; return AST_MODULE_LOAD_SUCCESS;
} }
@ -536,7 +520,7 @@ static int unload_module(void)
return tds_unload_module(); return tds_unload_module();
} }
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MSSQL CDR Backend", AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "FreeTDS CDR Backend",
.load = load_module, .load = load_module,
.unload = unload_module, .unload = unload_module,
.reload = reload, .reload = reload,

11903
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -1389,34 +1389,7 @@ fi
AST_EXT_TOOL_CHECK([GMIME], [gmime]) AST_EXT_TOOL_CHECK([GMIME], [gmime])
AST_EXT_LIB_CHECK([FREETDS], [tds], [tds_version], [tds.h]) AST_EXT_LIB_CHECK([FREETDS], [sybdb], [dbinit], [sybdb.h])
if test "${PBX_FREETDS}" != "0";
then
if test "${FREETDS_DIR}x" = "x";
then
for tds_dir in /usr /usr/local;
do
if test -f "${tds_dir}/include/tdsver.h";
then
FREETDS_DIR="${tds_dir}"
fi
done
fi
case `${GREP} TDS_VERSION_NO ${FREETDS_DIR:-/usr}/include/tdsver.h` in
*0.64*)
FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_64"
;;
*0.63*)
FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_63"
;;
*0.62*)
FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_0_62"
;;
*)
FREETDS_INCLUDE="${FREETDS_INCLUDE} -DFREETDS_PRE_0_62"
;;
esac
fi
AST_EXT_LIB_CHECK([TERMCAP], [termcap], [tgetent], []) AST_EXT_LIB_CHECK([TERMCAP], [termcap], [tgetent], [])

View File

@ -1176,9 +1176,6 @@
#ifndef _POSIX_PTHREAD_SEMANTICS #ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS
#endif #endif
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Define like PROTOTYPES; this can be used by system headers. */ /* Define like PROTOTYPES; this can be used by system headers. */
#undef __PROTOTYPES #undef __PROTOTYPES