From 8cd100de39390c3c59e55bcffbfb43816a09997f Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Wed, 1 Oct 2003 15:03:30 +0000 Subject: [PATCH] Add PGSQL support git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1594 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 2 + cdr/Makefile | 18 +++ cdr/cdr_pgsql.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100755 cdr/cdr_pgsql.c diff --git a/CHANGES b/CHANGES index 54e4daee81..628184f149 100755 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ + -- Remove MySQL and put PGSql instead for licensing reasons +Asterisk 0.5.0 -- Retain IAX2 and SIP registrations past shutdown/crash and restart -- True data mode bridging when possible -- H.323 build improvements diff --git a/cdr/Makefile b/cdr/Makefile index 368a8508ba..8b64bef3d8 100755 --- a/cdr/Makefile +++ b/cdr/Makefile @@ -15,6 +15,21 @@ MODS=cdr_csv.so CFLAGS+=-fPIC +# +# MySQL stuff... Autoconf anyone?? +# +MODS+=$(shell if [ -d /usr/local/pgsql/include ] || [ -d /usr/include/pgsql ] || [ -d /usr/local/include/pgsql ] || [ -d /opt/pgsql/include ] || [ -f /usr/include/libpq-fe.h ] ; then echo "cdr_pgsql.so"; fi) +CFLAGS+=$(shell if [ -d /usr/local/pgsql/include ]; then echo "-I/usr/local/pgsql/include"; fi) +CFLAGS+=$(shell if [ -d /usr/include/pgsql ]; then echo "-I/usr/include/pgsql"; fi) +CFLAGS+=$(shell if [ -d /usr/local/include/pgsql ]; then echo "-I/usr/local/include/pgsql"; fi) +CFLAGS+=$(shell if [ -d /opt/pgsql/include ]; then echo "-I/opt/pgsql/include"; fi) +CFLAGS+=$(shell if [ -f /usr/include/libpq-fe.h ]; then echo "-I/usr/include"; fi) +MLFLAGS= +MLFLAGS+=$(shell if [ -d /usr/lib/pgsql ]; then echo "-L/usr/lib/pgsql"; fi) +MLFLAGS+=$(shell if [ -d /usr/local/pgsql/lib ]; then echo "-L/usr/local/pgsql/lib"; fi) +MLFLAGS+=$(shell if [ -d /usr/local/lib/pgsql ]; then echo "-L/usr/local/lib/pgsql"; fi) +MLFLAGS+=$(shell if [ -d /opt/pgsql/lib ]; then echo "-L/opt/pgsql/lib"; fi) +MLFLAGS+=$(shell if [ -f /usr/lib/libpq.so ]; then echo "-L/usr/lib"; fi) all: depend $(MODS) @@ -31,6 +46,9 @@ ifneq ($(wildcard .depend),) include .depend endif +cdr_pgsql.so: cdr_pgsql.o + $(CC) -shared -Xlinker -x -o $@ $< -lpq -lz $(MLFLAGS) + depend: .depend .depend: diff --git a/cdr/cdr_pgsql.c b/cdr/cdr_pgsql.c new file mode 100755 index 0000000000..1d159af2d7 --- /dev/null +++ b/cdr/cdr_pgsql.c @@ -0,0 +1,324 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * PostgreSQL CDR logger + * + * Matthew D. Hardeman + * Adapted from the MySQL CDR logger originally by James Sharp + * + * Modified September 2003 + * Matthew D. Hardeman + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../asterisk.h" + +#include +#include + +#include +#include +#include + +#include +#include + +#define DATE_FORMAT "%Y-%m-%d %T" + +static char *desc = "PostgreSQL CDR Backend"; +static char *name = "pgsql"; +static char *config = "cdr_pgsql.conf"; +static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbsock = NULL, *pgdbport = NULL; +static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbport_alloc = 0; +static int connected = 0; + +static ast_mutex_t pgsql_lock = AST_MUTEX_INITIALIZER; + +PGconn *conn; +PGresult *result; + +static int pgsql_log(struct ast_cdr *cdr) +{ + struct tm tm; + struct timeval tv; + char sqlcmd[2048], timestr[128]; + time_t t; + + ast_mutex_lock(&pgsql_lock); + + memset(sqlcmd,0,2048); + + gettimeofday(&tv,NULL); + t = tv.tv_sec; + localtime_r(&t,&tm); + strftime(timestr,128,DATE_FORMAT,&tm); + + if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) { + conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); + if (PQstatus(conn) != CONNECTION_BAD) { + connected = 1; + } else { + ast_log(LOG_ERROR, "cdr_pgsql: cannot connect to database server %s. Call will not be logged\n", pghostname); + } + } else { + /* Test to be sure we're still connected... */ + /* If we're connected, and connection is working, good. */ + /* Otherwise, attempt reconnect. If it fails... sorry... */ + + if (PQstatus(conn) == CONNECTION_OK) { + connected = 1; + } else { + ast_log(LOG_ERROR, "cdr_pgsql: connection was lost... reattempting connection."); + PQreset(conn); + if (PQstatus(conn) == CONNECTION_OK) { + ast_log(LOG_ERROR, "cdr_pgsql: connection reestablished."); + connected = 1; + } else { + ast_log(LOG_ERROR, "cdr_pgsql: unable to reconnect to database."); + connected = 0; + } + } + + } + + if (connected) { + char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL; + char *uniqueid=NULL; + + /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */ + if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL) + PQescapeString(clid, cdr->clid, strlen(cdr->clid)); + if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL) + PQescapeString(dcontext, cdr->dcontext, strlen(cdr->dcontext)); + if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL) + PQescapeString(channel, cdr->channel, strlen(cdr->channel)); + if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL) + PQescapeString(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel)); + if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL) + PQescapeString(lastapp, cdr->lastapp, strlen(cdr->lastapp)); + if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL) + PQescapeString(lastdata, cdr->lastdata, strlen(cdr->lastdata)); + if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL) + PQescapeString(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid)); + + /* Check for all alloca failures above at once */ + if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) { + ast_log(LOG_ERROR, "cdr_pgsql: Out of memory error (insert fails)\n"); + ast_mutex_unlock(&pgsql_lock); + return -1; + } + + ast_log(LOG_DEBUG,"cdr_pgsql: inserting a CDR record.\n"); + + sprintf(sqlcmd,"INSERT INTO cdr (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')",timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid); + ast_log(LOG_DEBUG,"cdr_pgsql: SQL command as follows: %s\n",sqlcmd); + + result = PQexec(conn, sqlcmd); + if ( PQresultStatus(result) != PGRES_COMMAND_OK) { + ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database."); + ast_mutex_unlock(&pgsql_lock); + return -1; + } + } + ast_mutex_unlock(&pgsql_lock); + return 0; +} + +char *description(void) +{ + return desc; +} + +static int my_unload_module(void) +{ + PQfinish(conn); + connected = 0; + if (pghostname && hostname_alloc) { + free(pghostname); + pghostname = NULL; + hostname_alloc = 0; + } + if (pgdbname && dbname_alloc) { + free(pgdbname); + pgdbname = NULL; + dbname_alloc = 0; + } + if (pgdbuser && dbuser_alloc) { + free(pgdbuser); + pgdbuser = NULL; + dbuser_alloc = 0; + } + if (pgdbsock && dbsock_alloc) { + free(pgdbsock); + pgdbsock = NULL; + dbsock_alloc = 0; + } + if (pgpassword && password_alloc) { + free(pgpassword); + pgpassword = NULL; + password_alloc = 0; + } + if (pgdbport && dbport_alloc) { + free(pgdbport); + pgdbport = NULL; + dbport_alloc = 0; + } + ast_cdr_unregister(name); + return 0; +} + +static int my_load_module(void) +{ + int res; + struct ast_config *cfg; + struct ast_variable *var; + char *tmp; + + cfg = ast_load(config); + if (!cfg) { + ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config); + return 0; + } + + var = ast_variable_browse(cfg, "global"); + if (!var) { + /* nothing configured */ + return 0; + } + + tmp = ast_variable_retrieve(cfg,"global","hostname"); + if (tmp) { + pghostname = malloc(strlen(tmp) + 1); + if (pghostname != NULL) { + hostname_alloc = 1; + strcpy(pghostname,tmp); + } else { + ast_log(LOG_ERROR,"Out of memory error.\n"); + return -1; + } + } else { + ast_log(LOG_WARNING,"PostgreSQL server hostname not specified. Assuming localhost\n"); + pghostname = "localhost"; + } + + tmp = ast_variable_retrieve(cfg,"global","dbname"); + if (tmp) { + pgdbname = malloc(strlen(tmp) + 1); + if (pgdbname != NULL) { + dbname_alloc = 1; + strcpy(pgdbname,tmp); + } else { + ast_log(LOG_ERROR,"Out of memory error.\n"); + return -1; + } + } else { + ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asteriskcdrdb\n"); + pgdbname = "asteriskcdrdb"; + } + + tmp = ast_variable_retrieve(cfg,"global","user"); + if (tmp) { + pgdbuser = malloc(strlen(tmp) + 1); + if (pgdbuser != NULL) { + dbuser_alloc = 1; + strcpy(pgdbuser,tmp); + } else { + ast_log(LOG_ERROR,"Out of memory error.\n"); + return -1; + } + } else { + ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming root\n"); + pgdbuser = "root"; + } + + tmp = ast_variable_retrieve(cfg,"global","password"); + if (tmp) { + pgpassword = malloc(strlen(tmp) + 1); + if (pgpassword != NULL) { + password_alloc = 1; + strcpy(pgpassword,tmp); + } else { + ast_log(LOG_ERROR,"Out of memory error.\n"); + return -1; + } + } else { + ast_log(LOG_WARNING,"PostgreSQL database password not specified. Assuming blank\n"); + pgpassword = ""; + } + + tmp = ast_variable_retrieve(cfg,"global","port"); + if (tmp) { + pgdbport = malloc(strlen(tmp) + 1); + if (pgdbport != NULL) { + dbport_alloc = 1; + strcpy(pgdbport,tmp); + } else { + ast_log(LOG_ERROR,"Out of memory error.\n"); + return -1; + } + } else { + ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default.\n"); + pgdbport = "5432"; + } + + ast_destroy(cfg); + + ast_log(LOG_DEBUG,"cdr_pgsql: got hostname of %s\n",pghostname); + ast_log(LOG_DEBUG,"cdr_pgsql: got port of %s\n",pgdbport); + if (pgdbsock) + ast_log(LOG_DEBUG,"cdr_pgsql: got sock file of %s\n",pgdbsock); + ast_log(LOG_DEBUG,"cdr_pgsql: got user of %s\n",pgdbuser); + ast_log(LOG_DEBUG,"cdr_pgsql: got dbname of %s\n",pgdbname); + ast_log(LOG_DEBUG,"cdr_pgsql: got password of %s\n",pgpassword); + + conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); + if (PQstatus(conn) != CONNECTION_BAD) { + ast_log(LOG_DEBUG,"Successfully connected to PostgreSQL database.\n"); + connected = 1; + } else { + ast_log(LOG_ERROR, "cdr_pgsql: cannot connect to database server %s. Call will not be logged\n", pghostname); + connected = 0; + } + + res = ast_cdr_register(name, desc, pgsql_log); + if (res) { + ast_log(LOG_ERROR, "Unable to register PGSQL CDR handling\n"); + } + return res; +} + +int load_module(void) +{ + return my_load_module(); +} + +int unload_module(void) +{ + return my_unload_module(); +} + +int reload(void) +{ + my_unload_module(); + return my_load_module(); +} + +int usecount(void) +{ + return connected; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +}