2003-10-01 15:03:30 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
2003-10-01 15:03:30 +00:00
*
2006-01-24 11:42:09 +00:00
* Copyright (C) 2003 - 2006
2005-09-15 15:44:26 +00:00
*
2003-10-01 15:03:30 +00:00
* Matthew D. Hardeman <mhardemn@papersoft.com>
* Adapted from the MySQL CDR logger originally by James Sharp
*
* Modified September 2003
* Matthew D. Hardeman <mhardemn@papersoft.com>
*
2005-09-14 20:46:50 +00:00
* 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.
*
2003-10-01 15:03:30 +00:00
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-26 13:03:17 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-26 13:03:17 +00:00
* \brief PostgreSQL CDR logger
*
* \author Matthew D. Hardeman <mhardemn@papersoft.com>
2007-02-24 19:27:50 +00:00
* \extref PostgreSQL http://www.postgresql.org/
2003-10-01 15:03:30 +00:00
*
2005-10-26 13:03:17 +00:00
* See also
* \arg \ref Config_cdr
* \arg http://www.postgresql.org/
2005-11-06 15:09:47 +00:00
* \ingroup cdr_drivers
2003-10-01 15:03:30 +00:00
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
<depend>pgsql</depend>
***/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2003-10-01 15:03:30 +00:00
#include <time.h>
#include <libpq-fe.h>
2005-06-06 21:09:59 +00:00
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
2008-02-25 23:04:20 +00:00
#define DATE_FORMAT "'%Y-%m-%d %T'"
2003-10-01 15:03:30 +00:00
static char * name = "pgsql" ;
static char * config = "cdr_pgsql.conf" ;
2006-05-20 01:35:12 +00:00
static char * pghostname = NULL , * pgdbname = NULL , * pgdbuser = NULL , * pgpassword = NULL , * pgdbport = NULL , * table = NULL ;
2003-10-01 15:03:30 +00:00
static int connected = 0 ;
2008-02-25 23:04:20 +00:00
static int maxsize = 512 , maxsize2 = 512 ;
2003-10-01 15:03:30 +00:00
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC ( pgsql_lock );
2003-10-01 15:03:30 +00:00
2006-11-13 21:32:13 +00:00
static PGconn * conn = NULL ;
2003-10-01 15:03:30 +00:00
2008-02-25 23:04:20 +00:00
struct columns {
char * name ;
char * type ;
int len ;
2008-04-11 23:26:56 +00:00
unsigned int notnull : 1 ;
unsigned int hasdefault : 1 ;
2008-02-25 23:04:20 +00:00
AST_RWLIST_ENTRY ( columns ) list ;
};
static AST_RWLIST_HEAD_STATIC ( psql_columns , columns );
#define LENGTHEN_BUF1(size) \
do { \
/* Lengthen buffer, if necessary */ \
2008-08-24 16:23:15 +00:00
if (sql->used + size + 1 > sql->len) { \
if (ast_str_make_space(&sql, ((sql->len + size + 1) / 512 + 1) * 512) != 0) { \
2008-02-25 23:04:20 +00:00
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
ast_free(sql); \
ast_free(sql2); \
AST_RWLIST_UNLOCK(&psql_columns); \
return -1; \
} \
} \
} while (0)
#define LENGTHEN_BUF2(size) \
do { \
2008-08-24 16:23:15 +00:00
if (sql2->used + size + 1 > sql2->len) { \
if (ast_str_make_space(&sql2, ((sql2->len + size + 3) / 512 + 1) * 512) != 0) { \
2008-02-25 23:04:20 +00:00
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
ast_free(sql); \
ast_free(sql2); \
AST_RWLIST_UNLOCK(&psql_columns); \
return -1; \
} \
} \
} while (0)
2003-10-01 15:03:30 +00:00
static int pgsql_log ( struct ast_cdr * cdr )
{
2007-07-18 19:47:20 +00:00
struct ast_tm tm ;
2004-07-09 16:19:00 +00:00
char * pgerror ;
2007-06-12 19:40:41 +00:00
PGresult * result ;
2003-10-01 15:03:30 +00:00
ast_mutex_lock ( & pgsql_lock );
if (( ! connected ) && pghostname && pgdbuser && pgpassword && pgdbname ) {
conn = PQsetdbLogin ( pghostname , pgdbport , NULL , NULL , pgdbname , pgdbuser , pgpassword );
if ( PQstatus ( conn ) != CONNECTION_BAD ) {
connected = 1 ;
} else {
2003-11-05 05:56:27 +00:00
pgerror = PQerrorMessage ( conn );
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Unable to connect to database server %s. Calls will not be logged! \n " , pghostname );
ast_log ( LOG_ERROR , "Reason: %s \n " , pgerror );
2007-07-27 17:05:18 +00:00
PQfinish ( conn );
2007-07-26 18:31:28 +00:00
conn = NULL ;
2003-10-01 15:03:30 +00:00
}
}
if ( connected ) {
2008-02-25 23:04:20 +00:00
struct columns * cur ;
2008-08-24 16:23:15 +00:00
struct ast_str * sql = ast_str_create ( maxsize ), * sql2 = ast_str_create ( maxsize2 );
char buf [ 257 ], escapebuf [ 513 ], * value ;
2008-02-25 23:04:20 +00:00
2008-08-24 16:23:15 +00:00
if ( ! sql || ! sql2 ) {
if ( sql ) {
ast_free ( sql );
}
if ( sql2 ) {
ast_free ( sql2 );
}
return - 1 ;
}
ast_str_set ( & sql , 0 , "INSERT INTO %s (" , table );
ast_str_set ( & sql2 , 0 , " VALUES (" );
2008-02-25 23:04:20 +00:00
AST_RWLIST_RDLOCK ( & psql_columns );
AST_RWLIST_TRAVERSE ( & psql_columns , cur , list ) {
/* For fields not set, simply skip them */
ast_cdr_getvar ( cdr , cur -> name , & value , buf , sizeof ( buf ), 0 , 0 );
2008-05-10 14:19:41 +00:00
if ( strcmp ( cur -> name , "calldate" ) == 0 && ! value ) {
ast_cdr_getvar ( cdr , "start" , & value , buf , sizeof ( buf ), 0 , 0 );
}
2008-04-11 23:26:56 +00:00
if ( ! value ) {
if ( cur -> notnull && ! cur -> hasdefault ) {
/* Field is NOT NULL (but no default), must include it anyway */
2008-06-29 12:06:46 +00:00
LENGTHEN_BUF1 ( strlen ( cur -> name ) + 2 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql , 0 , " \" %s \" ," , cur -> name );
2008-04-11 23:26:56 +00:00
LENGTHEN_BUF2 ( 3 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "''," );
2008-04-11 23:26:56 +00:00
}
2008-02-25 23:04:20 +00:00
continue ;
2008-04-11 23:26:56 +00:00
}
2008-02-25 23:04:20 +00:00
2008-06-29 12:06:46 +00:00
LENGTHEN_BUF1 ( strlen ( cur -> name ) + 2 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql , 0 , " \" %s \" ," , cur -> name );
2008-02-25 23:04:20 +00:00
if ( strcmp ( cur -> name , "start" ) == 0 || strcmp ( cur -> name , "calldate" ) == 0 ) {
if ( strncmp ( cur -> type , "int" , 3 ) == 0 ) {
LENGTHEN_BUF2 ( 12 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%ld" , cdr -> start . tv_sec );
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur -> type , "float" , 5 ) == 0 ) {
LENGTHEN_BUF2 ( 30 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%f" , ( double ) cdr -> start . tv_sec + ( double ) cdr -> start . tv_usec / 1000000.0 );
2008-02-25 23:04:20 +00:00
} else {
/* char, hopefully */
LENGTHEN_BUF2 ( 30 );
ast_localtime ( & cdr -> start , & tm , NULL );
2008-08-24 16:23:15 +00:00
ast_strftime ( buf , sizeof ( buf ), DATE_FORMAT , & tm );
ast_str_append ( & sql2 , 0 , "%s" , buf );
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur -> name , "answer" ) == 0 ) {
if ( strncmp ( cur -> type , "int" , 3 ) == 0 ) {
LENGTHEN_BUF2 ( 12 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%ld" , cdr -> answer . tv_sec );
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur -> type , "float" , 5 ) == 0 ) {
LENGTHEN_BUF2 ( 30 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%f" , ( double ) cdr -> answer . tv_sec + ( double ) cdr -> answer . tv_usec / 1000000.0 );
2008-02-25 23:04:20 +00:00
} else {
/* char, hopefully */
LENGTHEN_BUF2 ( 30 );
ast_localtime ( & cdr -> start , & tm , NULL );
2008-08-24 16:23:15 +00:00
ast_strftime ( buf , sizeof ( buf ), DATE_FORMAT , & tm );
ast_str_append ( & sql2 , 0 , "%s" , buf );
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur -> name , "end" ) == 0 ) {
if ( strncmp ( cur -> type , "int" , 3 ) == 0 ) {
LENGTHEN_BUF2 ( 12 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%ld" , cdr -> end . tv_sec );
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur -> type , "float" , 5 ) == 0 ) {
LENGTHEN_BUF2 ( 30 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%f" , ( double ) cdr -> end . tv_sec + ( double ) cdr -> end . tv_usec / 1000000.0 );
2008-02-25 23:04:20 +00:00
} else {
/* char, hopefully */
LENGTHEN_BUF2 ( 30 );
ast_localtime ( & cdr -> end , & tm , NULL );
2008-08-24 16:23:15 +00:00
ast_strftime ( buf , sizeof ( buf ), DATE_FORMAT , & tm );
ast_str_append ( & sql2 , 0 , "%s" , buf );
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur -> name , "duration" ) == 0 || strcmp ( cur -> name , "billsec" ) == 0 ) {
if ( cur -> type [ 0 ] == 'i' ) {
/* Get integer, no need to escape anything */
ast_cdr_getvar ( cdr , cur -> name , & value , buf , sizeof ( buf ), 0 , 0 );
LENGTHEN_BUF2 ( 12 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%s" , value );
2008-02-25 23:04:20 +00:00
} else if ( strncmp ( cur -> type , "float" , 5 ) == 0 ) {
2008-08-07 00:52:23 +00:00
struct timeval * when = cur -> name [ 0 ] == 'd' ? & cdr -> start : & cdr -> answer ;
2008-02-25 23:04:20 +00:00
LENGTHEN_BUF2 ( 30 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%f" , ( double ) cdr -> end . tv_sec - when -> tv_sec + cdr -> end . tv_usec / 1000000.0 - when -> tv_usec / 1000000.0 );
2008-02-25 23:04:20 +00:00
} else {
/* Char field, probably */
2008-08-07 00:52:23 +00:00
struct timeval * when = cur -> name [ 0 ] == 'd' ? & cdr -> start : & cdr -> answer ;
2008-02-25 23:04:20 +00:00
LENGTHEN_BUF2 ( 30 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "'%f'" , ( double ) cdr -> end . tv_sec - when -> tv_sec + cdr -> end . tv_usec / 1000000.0 - when -> tv_usec / 1000000.0 );
2008-02-25 23:04:20 +00:00
}
} else if ( strcmp ( cur -> name , "disposition" ) == 0 || strcmp ( cur -> name , "amaflags" ) == 0 ) {
if ( strncmp ( cur -> type , "int" , 3 ) == 0 ) {
/* Integer, no need to escape anything */
ast_cdr_getvar ( cdr , cur -> name , & value , buf , sizeof ( buf ), 0 , 1 );
LENGTHEN_BUF2 ( 12 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%s" , value );
2008-02-25 23:04:20 +00:00
} else {
/* Although this is a char field, there are no special characters in the values for these fields */
ast_cdr_getvar ( cdr , cur -> name , & value , buf , sizeof ( buf ), 0 , 0 );
LENGTHEN_BUF2 ( 30 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "'%s'" , value );
2008-02-25 23:04:20 +00:00
}
} else {
/* Arbitrary field, could be anything */
ast_cdr_getvar ( cdr , cur -> name , & value , buf , sizeof ( buf ), 0 , 0 );
if ( strncmp ( cur -> type , "int" , 3 ) == 0 ) {
long long whatever ;
if ( value && sscanf ( value , "%lld" , & whatever ) == 1 ) {
LENGTHEN_BUF2 ( 25 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%lld" , whatever );
2008-02-25 23:04:20 +00:00
} else {
LENGTHEN_BUF2 ( 1 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "0" );
2008-02-25 23:04:20 +00:00
}
} else if ( strncmp ( cur -> type , "float" , 5 ) == 0 ) {
long double whatever ;
if ( value && sscanf ( value , "%Lf" , & whatever ) == 1 ) {
LENGTHEN_BUF2 ( 50 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "%30Lf" , whatever );
2008-02-25 23:04:20 +00:00
} else {
LENGTHEN_BUF2 ( 1 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "0" );
2008-02-25 23:04:20 +00:00
}
/* XXX Might want to handle dates, times, and other misc fields here XXX */
} else {
if ( value )
PQescapeStringConn ( conn , escapebuf , value , strlen ( value ), NULL );
else
escapebuf [ 0 ] = '\0' ;
LENGTHEN_BUF2 ( strlen ( escapebuf ) + 2 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "'%s'" , escapebuf );
2008-02-25 23:04:20 +00:00
}
}
LENGTHEN_BUF2 ( 1 );
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql2 , 0 , "," );
2008-02-25 23:04:20 +00:00
}
AST_RWLIST_UNLOCK ( & psql_columns );
2008-08-24 16:23:15 +00:00
LENGTHEN_BUF1 ( sql2 -> len );
2008-08-27 23:23:56 +00:00
sql -> str [ sql -> used - 1 ] = ')' ;
sql2 -> str [ sql2 -> used - 1 ] = ')' ;
2008-08-24 16:23:15 +00:00
ast_str_append ( & sql , 0 , "%s" , sql2 -> str );
ast_verb ( 11 , "[%s] \n " , sql -> str );
2003-10-01 15:03:30 +00:00
2008-04-20 14:52:07 +00:00
ast_debug ( 2 , "inserting a CDR record. \n " );
2003-10-01 15:03:30 +00:00
2003-11-05 05:56:27 +00:00
/* 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 {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Connection was lost... attempting to reconnect. \n " );
2003-11-05 05:56:27 +00:00
PQreset ( conn );
if ( PQstatus ( conn ) == CONNECTION_OK ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Connection reestablished. \n " );
2003-11-05 05:56:27 +00:00
connected = 1 ;
} else {
pgerror = PQerrorMessage ( conn );
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Unable to reconnect to database server %s. Calls will not be logged! \n " , pghostname );
ast_log ( LOG_ERROR , "Reason: %s \n " , pgerror );
2007-07-27 17:05:18 +00:00
PQfinish ( conn );
2007-07-26 18:31:28 +00:00
conn = NULL ;
2003-11-05 05:56:27 +00:00
connected = 0 ;
2004-01-22 17:03:11 +00:00
ast_mutex_unlock ( & pgsql_lock );
2008-08-24 16:26:48 +00:00
ast_free ( sql );
ast_free ( sql2 );
2003-11-05 05:56:27 +00:00
return - 1 ;
}
}
2008-08-24 16:23:15 +00:00
result = PQexec ( conn , sql -> str );
2007-06-12 19:40:41 +00:00
if ( PQresultStatus ( result ) != PGRES_COMMAND_OK ) {
pgerror = PQresultErrorMessage ( result );
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Failed to insert call detail record into database! \n " );
ast_log ( LOG_ERROR , "Reason: %s \n " , pgerror );
ast_log ( LOG_ERROR , "Connection may have been lost... attempting to reconnect. \n " );
2005-02-19 20:10:52 +00:00
PQreset ( conn );
if ( PQstatus ( conn ) == CONNECTION_OK ) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Connection reestablished. \n " );
2005-02-19 20:10:52 +00:00
connected = 1 ;
2007-06-20 23:33:49 +00:00
PQclear ( result );
2008-08-24 16:23:15 +00:00
result = PQexec ( conn , sql -> str );
2007-06-12 19:40:41 +00:00
if ( PQresultStatus ( result ) != PGRES_COMMAND_OK ) {
2005-02-19 20:10:52 +00:00
pgerror = PQresultErrorMessage ( result );
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD! \n " );
ast_log ( LOG_ERROR , "Reason: %s \n " , pgerror );
2005-02-19 20:10:52 +00:00
}
}
2003-10-01 15:03:30 +00:00
ast_mutex_unlock ( & pgsql_lock );
2007-06-12 19:40:41 +00:00
PQclear ( result );
2008-08-24 16:23:15 +00:00
ast_free ( sql );
ast_free ( sql2 );
2003-10-01 15:03:30 +00:00
return - 1 ;
}
2007-06-12 19:40:41 +00:00
PQclear ( result );
2008-08-24 16:23:15 +00:00
ast_free ( sql );
ast_free ( sql2 );
2003-10-01 15:03:30 +00:00
}
ast_mutex_unlock ( & pgsql_lock );
return 0 ;
}
2007-08-16 21:09:46 +00:00
static int unload_module ( void )
2008-02-25 23:04:20 +00:00
{
2008-08-07 00:52:23 +00:00
struct columns * current ;
2008-02-25 23:04:20 +00:00
ast_cdr_unregister ( name );
/* Give all threads time to finish */
usleep ( 1 );
2007-07-26 18:31:28 +00:00
PQfinish ( conn );
2008-02-25 23:04:20 +00:00
2005-07-11 21:06:27 +00:00
if ( pghostname )
2007-06-06 21:20:11 +00:00
ast_free ( pghostname );
2005-07-11 21:06:27 +00:00
if ( pgdbname )
2007-06-06 21:20:11 +00:00
ast_free ( pgdbname );
2005-07-11 21:06:27 +00:00
if ( pgdbuser )
2007-06-06 21:20:11 +00:00
ast_free ( pgdbuser );
2005-07-11 21:06:27 +00:00
if ( pgpassword )
2007-06-06 21:20:11 +00:00
ast_free ( pgpassword );
2005-07-11 21:06:27 +00:00
if ( pgdbport )
2007-06-06 21:20:11 +00:00
ast_free ( pgdbport );
2005-07-11 21:06:27 +00:00
if ( table )
2007-06-06 21:20:11 +00:00
ast_free ( table );
2008-02-25 23:04:20 +00:00
AST_RWLIST_WRLOCK ( & psql_columns );
2008-08-07 00:52:23 +00:00
while (( current = AST_RWLIST_REMOVE_HEAD ( & psql_columns , list ))) {
ast_free ( current );
2008-02-25 23:04:20 +00:00
}
AST_RWLIST_UNLOCK ( & psql_columns );
2003-10-01 15:03:30 +00:00
return 0 ;
}
2007-08-16 21:09:46 +00:00
static int config_module ( int reload )
2003-10-01 15:03:30 +00:00
{
struct ast_variable * var ;
2007-08-16 21:09:46 +00:00
char * pgerror ;
2008-02-25 23:04:20 +00:00
struct columns * cur ;
PGresult * result ;
2006-09-20 21:03:37 +00:00
const char * tmp ;
2007-08-16 21:09:46 +00:00
struct ast_config * cfg ;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
if (( cfg = ast_config_load ( config , config_flags )) == NULL ) {
ast_log ( LOG_WARNING , "Unable to load config for PostgreSQL CDR's: %s \n " , config );
return - 1 ;
} else if ( cfg == CONFIG_STATUS_FILEUNCHANGED )
return 0 ;
2003-10-01 15:03:30 +00:00
2007-09-20 16:10:57 +00:00
if ( ! ( var = ast_variable_browse ( cfg , "global" ))) {
ast_config_destroy ( cfg );
2003-10-01 15:03:30 +00:00
return 0 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2007-08-16 21:09:46 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , "global" , "hostname" ))) {
ast_log ( LOG_WARNING , "PostgreSQL server hostname not specified. Assuming unix socket connection \n " );
2006-05-20 00:57:04 +00:00
tmp = "" ; /* connect via UNIX-socket by default */
2005-07-11 21:06:27 +00:00
}
2007-08-16 21:09:46 +00:00
if ( pghostname )
ast_free ( pghostname );
2007-09-20 16:10:57 +00:00
if ( ! ( pghostname = ast_strdup ( tmp ))) {
ast_config_destroy ( cfg );
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , "global" , "dbname" ))) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , "PostgreSQL database not specified. Assuming asterisk \n " );
2005-07-11 21:06:27 +00:00
tmp = "asteriskcdrdb" ;
}
2006-05-20 01:29:08 +00:00
2007-08-16 21:09:46 +00:00
if ( pgdbname )
ast_free ( pgdbname );
2007-09-20 16:10:57 +00:00
if ( ! ( pgdbname = ast_strdup ( tmp ))) {
ast_config_destroy ( cfg );
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , "global" , "user" ))) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , "PostgreSQL database user not specified. Assuming asterisk \n " );
2006-05-20 01:35:12 +00:00
tmp = "asterisk" ;
2005-07-11 21:06:27 +00:00
}
2006-05-20 01:29:08 +00:00
2007-08-16 21:09:46 +00:00
if ( pgdbuser )
ast_free ( pgdbuser );
2007-09-20 16:10:57 +00:00
if ( ! ( pgdbuser = ast_strdup ( tmp ))) {
ast_config_destroy ( cfg );
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , "global" , "password" ))) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , "PostgreSQL database password not specified. Assuming blank \n " );
2005-07-11 21:06:27 +00:00
tmp = "" ;
}
2006-05-20 01:29:08 +00:00
2007-08-16 21:09:46 +00:00
if ( pgpassword )
ast_free ( pgpassword );
2007-09-20 16:10:57 +00:00
if ( ! ( pgpassword = ast_strdup ( tmp ))) {
ast_config_destroy ( cfg );
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2008-04-20 14:52:07 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , "global" , "port" ))) {
ast_log ( LOG_WARNING , "PostgreSQL database port not specified. Using default 5432. \n " );
2005-07-11 21:06:27 +00:00
tmp = "5432" ;
}
2006-05-20 01:29:08 +00:00
2007-08-16 21:09:46 +00:00
if ( pgdbport )
ast_free ( pgdbport );
2007-09-20 16:10:57 +00:00
if ( ! ( pgdbport = ast_strdup ( tmp ))) {
ast_config_destroy ( cfg );
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2005-07-11 21:06:27 +00:00
2006-05-20 01:29:08 +00:00
if ( ! ( tmp = ast_variable_retrieve ( cfg , "global" , "table" ))) {
2008-04-20 14:52:07 +00:00
ast_log ( LOG_WARNING , "CDR table not specified. Assuming cdr \n " );
2005-07-11 21:06:27 +00:00
tmp = "cdr" ;
}
2006-05-20 01:29:08 +00:00
2007-08-16 21:09:46 +00:00
if ( table )
ast_free ( table );
2007-09-20 16:10:57 +00:00
if ( ! ( table = ast_strdup ( tmp ))) {
ast_config_destroy ( cfg );
2005-07-11 21:06:27 +00:00
return - 1 ;
2007-09-20 16:10:57 +00:00
}
2003-10-01 15:03:30 +00:00
2006-01-24 11:46:29 +00:00
if ( option_debug ) {
2008-03-11 16:28:03 +00:00
if ( ast_strlen_zero ( pghostname )) {
2008-04-20 14:52:07 +00:00
ast_debug ( 1 , "using default unix socket \n " );
2008-03-11 16:28:03 +00:00
} else {
2008-04-20 14:52:07 +00:00
ast_debug ( 1 , "got hostname of %s \n " , pghostname );
2008-03-11 16:28:03 +00:00
}
2008-04-20 14:52:07 +00:00
ast_debug ( 1 , "got port of %s \n " , pgdbport );
ast_debug ( 1 , "got user of %s \n " , pgdbuser );
ast_debug ( 1 , "got dbname of %s \n " , pgdbname );
ast_debug ( 1 , "got password of %s \n " , pgpassword );
ast_debug ( 1 , "got sql table name of %s \n " , table );
2006-01-24 11:46:29 +00:00
}
2008-03-11 16:28:03 +00:00
2003-10-01 15:03:30 +00:00
conn = PQsetdbLogin ( pghostname , pgdbport , NULL , NULL , pgdbname , pgdbuser , pgpassword );
if ( PQstatus ( conn ) != CONNECTION_BAD ) {
2008-04-15 20:51:08 +00:00
char sqlcmd [ 512 ];
2008-04-11 23:26:56 +00:00
char * fname , * ftype , * flen , * fnotnull , * fdef ;
2008-02-25 23:04:20 +00:00
int i , rows ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Successfully connected to PostgreSQL database. \n " );
2003-10-01 15:03:30 +00:00
connected = 1 ;
2008-02-25 23:04:20 +00:00
/* Query the columns */
2008-04-11 23:26:56 +00:00
snprintf ( sqlcmd , sizeof ( sqlcmd ), "select a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc from pg_class c, pg_type t, pg_attribute a left outer join pg_attrdef d on a.atthasdef and d.adrelid = a.attrelid and d.adnum = a.attnum where c.oid = a.attrelid and a.atttypid = t.oid and (a.attnum > 0) and c.relname = '%s' order by c.relname, attnum" , table );
2008-02-25 23:04:20 +00:00
result = PQexec ( conn , sqlcmd );
if ( PQresultStatus ( result ) != PGRES_TUPLES_OK ) {
pgerror = PQresultErrorMessage ( result );
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Failed to query database columns: %s \n " , pgerror );
2008-02-25 23:04:20 +00:00
PQclear ( result );
unload_module ();
return AST_MODULE_LOAD_DECLINE ;
}
rows = PQntuples ( result );
for ( i = 0 ; i < rows ; i ++ ) {
fname = PQgetvalue ( result , i , 0 );
ftype = PQgetvalue ( result , i , 1 );
flen = PQgetvalue ( result , i , 2 );
2008-04-11 23:26:56 +00:00
fnotnull = PQgetvalue ( result , i , 3 );
fdef = PQgetvalue ( result , i , 4 );
2008-02-25 23:04:20 +00:00
ast_verb ( 4 , "Found column '%s' of type '%s' \n " , fname , ftype );
cur = ast_calloc ( 1 , sizeof ( * cur ) + strlen ( fname ) + strlen ( ftype ) + 2 );
if ( cur ) {
sscanf ( flen , "%d" , & cur -> len );
cur -> name = ( char * ) cur + sizeof ( * cur );
cur -> type = ( char * ) cur + sizeof ( * cur ) + strlen ( fname ) + 1 ;
strcpy ( cur -> name , fname );
strcpy ( cur -> type , ftype );
2008-04-11 23:26:56 +00:00
if ( * fnotnull == 't' ) {
cur -> notnull = 1 ;
} else {
cur -> notnull = 0 ;
}
if ( ! ast_strlen_zero ( fdef )) {
cur -> hasdefault = 1 ;
} else {
cur -> hasdefault = 0 ;
}
2008-02-25 23:04:20 +00:00
AST_RWLIST_INSERT_TAIL ( & psql_columns , cur , list );
}
}
PQclear ( result );
2003-10-01 15:03:30 +00:00
} else {
2007-08-16 21:09:46 +00:00
pgerror = PQerrorMessage ( conn );
2008-04-20 14:52:07 +00:00
ast_log ( LOG_ERROR , "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!! \n " , pghostname );
ast_log ( LOG_ERROR , "Reason: %s \n " , pgerror );
2003-10-01 15:03:30 +00:00
connected = 0 ;
}
2007-09-20 16:10:57 +00:00
ast_config_destroy ( cfg );
2006-08-21 02:11:39 +00:00
return ast_cdr_register ( name , ast_module_info -> description , pgsql_log );
2003-10-01 15:03:30 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2003-10-01 15:03:30 +00:00
{
2007-08-16 21:09:46 +00:00
return config_module ( 0 ) ? AST_MODULE_LOAD_DECLINE : 0 ;
2003-10-01 15:03:30 +00:00
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2003-10-01 15:03:30 +00:00
{
2007-08-16 21:09:46 +00:00
return config_module ( 1 );
2003-10-01 15:03:30 +00:00
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , "PostgreSQL CDR Backend" ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
);