2006-04-05 17:46:09 +00:00
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Copyright (C) 1999-2005, Digium, Inc.
*
2007-10-19 18:01:00 +00:00
* Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
2006-04-05 17:46:09 +00:00
* Mark Spencer <markster@digium.com> - Asterisk Author
* Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
*
2007-10-19 18:01:00 +00:00
* res_config_pgsql.c <PostgreSQL plugin for RealTime configuration engine>
2006-04-05 17:46:09 +00:00
*
* v1.0 - (07-11-05) - Initial version based on res_config_mysql v2.0
*/
/*! \file
*
2007-10-19 18:01:00 +00:00
* \brief PostgreSQL plugin for Asterisk RealTime Architecture
2006-04-05 17:46:09 +00:00
*
* \author Mark Spencer <markster@digium.com>
2007-10-19 18:01:00 +00:00
* \author Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
2006-04-05 17:46:09 +00:00
*
2009-10-07 18:55:25 +00:00
* \extref PostgreSQL http://www.postgresql.org
2006-04-05 17:46:09 +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$" )
2006-04-10 02:01:39 +00:00
#include <libpq-fe.h> /* PostgreSQL */
2006-04-05 17:46:09 +00:00
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
AST_MUTEX_DEFINE_STATIC ( pgsql_lock );
2008-10-14 00:08:52 +00:00
AST_THREADSTORAGE ( sql_buf );
AST_THREADSTORAGE ( findtable_buf );
AST_THREADSTORAGE ( where_buf );
AST_THREADSTORAGE ( escapebuf_buf );
2006-04-10 02:01:39 +00:00
2006-04-05 17:46:09 +00:00
#define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
2006-04-10 02:01:39 +00:00
2009-06-15 17:34:30 +00:00
static PGconn * pgsqlConn = NULL ;
2009-10-06 19:31:39 +00:00
static int version ;
#define has_schema_support (version > 70300 ? 1 : 0)
2006-04-10 02:01:39 +00:00
2006-04-09 18:57:25 +00:00
#define MAX_DB_OPTION_SIZE 64
2006-04-10 02:01:39 +00:00
2008-06-05 19:07:27 +00:00
struct columns {
char * name ;
char * type ;
int len ;
unsigned int notnull : 1 ;
unsigned int hasdefault : 1 ;
AST_LIST_ENTRY ( columns ) list ;
};
struct tables {
2008-10-14 00:08:52 +00:00
ast_rwlock_t lock ;
2008-06-05 19:07:27 +00:00
AST_LIST_HEAD_NOLOCK ( psql_columns , columns ) columns ;
AST_LIST_ENTRY ( tables ) list ;
char name [ 0 ];
};
static AST_LIST_HEAD_STATIC ( psql_tables , tables );
2006-04-10 02:01:39 +00:00
static char dbhost [ MAX_DB_OPTION_SIZE ] = "" ;
static char dbuser [ MAX_DB_OPTION_SIZE ] = "" ;
static char dbpass [ MAX_DB_OPTION_SIZE ] = "" ;
static char dbname [ MAX_DB_OPTION_SIZE ] = "" ;
static char dbsock [ MAX_DB_OPTION_SIZE ] = "" ;
static int dbport = 5432 ;
static time_t connect_time = 0 ;
2006-04-05 17:46:09 +00:00
2007-08-16 21:09:46 +00:00
static int parse_config ( int reload );
2006-04-05 17:46:09 +00:00
static int pgsql_reconnect ( const char * database );
2007-10-19 18:01:00 +00:00
static char * handle_cli_realtime_pgsql_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a );
2008-06-05 19:07:27 +00:00
static char * handle_cli_realtime_pgsql_cache ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a );
2009-06-15 17:34:30 +00:00
static enum { RQ_WARN , RQ_CREATECLOSE , RQ_CREATECHAR } requirements ;
2006-04-05 17:46:09 +00:00
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_realtime [] = {
2007-10-22 20:05:18 +00:00
AST_CLI_DEFINE ( handle_cli_realtime_pgsql_status , "Shows connection information for the PostgreSQL RealTime driver" ),
2008-06-05 19:07:27 +00:00
AST_CLI_DEFINE ( handle_cli_realtime_pgsql_cache , "Shows cached tables within the PostgreSQL realtime driver" ),
2006-09-18 19:54:18 +00:00
};
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
#define ESCAPE_STRING(buffer, stringname) \
do { \
int len; \
2008-12-13 08:36:35 +00:00
if ((len = strlen(stringname)) > (ast_str_size(buffer) - 1) / 2) { \
2008-10-14 00:08:52 +00:00
ast_str_make_space(&buffer, len * 2 + 1); \
} \
2008-12-13 08:36:35 +00:00
PQescapeStringConn(pgsqlConn, ast_str_buffer(buffer), stringname, len, &pgresult); \
2008-10-14 00:08:52 +00:00
} while (0)
2008-06-05 19:07:27 +00:00
static void destroy_table ( struct tables * table )
{
struct columns * column ;
2008-10-14 00:08:52 +00:00
ast_rwlock_wrlock ( & table -> lock );
2008-06-05 19:07:27 +00:00
while (( column = AST_LIST_REMOVE_HEAD ( & table -> columns , list ))) {
ast_free ( column );
}
2008-10-14 00:08:52 +00:00
ast_rwlock_unlock ( & table -> lock );
ast_rwlock_destroy ( & table -> lock );
2008-06-05 19:07:27 +00:00
ast_free ( table );
}
2009-10-06 19:31:39 +00:00
static struct tables * find_table ( const char * orig_tablename )
2008-06-05 19:07:27 +00:00
{
struct columns * column ;
struct tables * table ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & findtable_buf , 330 );
2008-06-05 19:07:27 +00:00
char * pgerror ;
PGresult * result ;
char * fname , * ftype , * flen , * fnotnull , * fdef ;
int i , rows ;
AST_LIST_LOCK ( & psql_tables );
AST_LIST_TRAVERSE ( & psql_tables , table , list ) {
2009-10-06 19:31:39 +00:00
if ( ! strcasecmp ( table -> name , orig_tablename )) {
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , "Found table in cache; now locking \n " );
2008-10-14 00:08:52 +00:00
ast_rwlock_rdlock ( & table -> lock );
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , "Lock cached table; now returning \n " );
AST_LIST_UNLOCK ( & psql_tables );
return table ;
}
}
2009-10-06 19:31:39 +00:00
ast_debug ( 1 , "Table '%s' not found in cache, querying now \n " , orig_tablename );
2008-06-05 19:07:27 +00:00
/* Not found, scan the table */
2009-10-06 19:31:39 +00:00
if ( has_schema_support ) {
char * schemaname , * tablename ;
if ( strchr ( orig_tablename , '.' )) {
schemaname = ast_strdupa ( orig_tablename );
tablename = strchr ( schemaname , '.' );
* tablename ++ = '\0' ;
} else {
schemaname = "" ;
tablename = ast_strdupa ( orig_tablename );
}
/* Escape special characters in schemaname */
if ( strchr ( schemaname , '\\' ) || strchr ( schemaname , '\'' )) {
char * tmp = schemaname , * ptr ;
ptr = schemaname = alloca ( strlen ( tmp ) * 2 + 1 );
for (; * tmp ; tmp ++ ) {
if ( strchr ( " \\ '" , * tmp )) {
* ptr ++ = * tmp ;
}
* ptr ++ = * tmp ;
}
* ptr = '\0' ;
}
/* Escape special characters in tablename */
if ( strchr ( tablename , '\\' ) || strchr ( tablename , '\'' )) {
char * tmp = tablename , * ptr ;
ptr = tablename = alloca ( strlen ( tmp ) * 2 + 1 );
for (; * tmp ; tmp ++ ) {
if ( strchr ( " \\ '" , * tmp )) {
* ptr ++ = * tmp ;
}
* ptr ++ = * tmp ;
}
* ptr = '\0' ;
}
ast_str_set ( & sql , 0 , "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum" ,
tablename ,
ast_strlen_zero ( schemaname ) ? "" : "'" , ast_strlen_zero ( schemaname ) ? "current_schema()" : schemaname , ast_strlen_zero ( schemaname ) ? "" : "'" );
} else {
/* Escape special characters in tablename */
if ( strchr ( orig_tablename , '\\' ) || strchr ( orig_tablename , '\'' )) {
const char * tmp = orig_tablename ;
char * ptr ;
orig_tablename = ptr = alloca ( strlen ( tmp ) * 2 + 1 );
for (; * tmp ; tmp ++ ) {
if ( strchr ( " \\ '" , * tmp )) {
* ptr ++ = * tmp ;
}
* ptr ++ = * tmp ;
}
* ptr = '\0' ;
}
ast_str_set ( & sql , 0 , "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod 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" , orig_tablename );
}
2008-12-13 08:36:35 +00:00
result = PQexec ( pgsqlConn , ast_str_buffer ( sql ));
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , "Query of table structure complete. Now retrieving results. \n " );
if ( PQresultStatus ( result ) != PGRES_TUPLES_OK ) {
pgerror = PQresultErrorMessage ( result );
ast_log ( LOG_ERROR , "Failed to query database columns: %s \n " , pgerror );
PQclear ( result );
AST_LIST_UNLOCK ( & psql_tables );
return NULL ;
}
2009-10-06 19:31:39 +00:00
if ( ! ( table = ast_calloc ( 1 , sizeof ( * table ) + strlen ( orig_tablename ) + 1 ))) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_ERROR , "Unable to allocate memory for new table structure \n " );
AST_LIST_UNLOCK ( & psql_tables );
return NULL ;
}
2009-10-06 19:31:39 +00:00
strcpy ( table -> name , orig_tablename ); /* SAFE */
2008-10-14 00:08:52 +00:00
ast_rwlock_init ( & table -> lock );
2008-06-05 19:07:27 +00:00
AST_LIST_HEAD_INIT_NOLOCK ( & table -> columns );
2008-10-14 00:08:52 +00:00
2008-06-05 19:07:27 +00:00
rows = PQntuples ( result );
for ( i = 0 ; i < rows ; i ++ ) {
fname = PQgetvalue ( result , i , 0 );
ftype = PQgetvalue ( result , i , 1 );
flen = PQgetvalue ( result , i , 2 );
fnotnull = PQgetvalue ( result , i , 3 );
fdef = PQgetvalue ( result , i , 4 );
ast_verb ( 4 , "Found column '%s' of type '%s' \n " , fname , ftype );
if ( ! ( column = ast_calloc ( 1 , sizeof ( * column ) + strlen ( fname ) + strlen ( ftype ) + 2 ))) {
2009-10-06 19:31:39 +00:00
ast_log ( LOG_ERROR , "Unable to allocate column element for %s, %s \n " , orig_tablename , fname );
2008-06-05 19:07:27 +00:00
destroy_table ( table );
AST_LIST_UNLOCK ( & psql_tables );
return NULL ;
}
2008-06-13 21:50:28 +00:00
if ( strcmp ( flen , "-1" ) == 0 ) {
/* Some types, like chars, have the length stored in a different field */
flen = PQgetvalue ( result , i , 5 );
2009-08-10 19:20:57 +00:00
sscanf ( flen , "%30d" , & column -> len );
2008-06-13 21:50:28 +00:00
column -> len -= 4 ;
} else {
2009-08-10 19:20:57 +00:00
sscanf ( flen , "%30d" , & column -> len );
2008-06-13 21:50:28 +00:00
}
2008-06-05 19:07:27 +00:00
column -> name = ( char * ) column + sizeof ( * column );
column -> type = ( char * ) column + sizeof ( * column ) + strlen ( fname ) + 1 ;
strcpy ( column -> name , fname );
strcpy ( column -> type , ftype );
if ( * fnotnull == 't' ) {
column -> notnull = 1 ;
} else {
column -> notnull = 0 ;
}
if ( ! ast_strlen_zero ( fdef )) {
column -> hasdefault = 1 ;
} else {
column -> hasdefault = 0 ;
}
AST_LIST_INSERT_TAIL ( & table -> columns , column , list );
}
PQclear ( result );
AST_LIST_INSERT_TAIL ( & psql_tables , table , list );
2008-10-14 00:08:52 +00:00
ast_rwlock_rdlock ( & table -> lock );
2008-06-05 19:07:27 +00:00
AST_LIST_UNLOCK ( & psql_tables );
return table ;
}
2008-10-14 00:08:52 +00:00
#define release_table(table) ast_rwlock_unlock(&(table)->lock);
static struct columns * find_column ( struct tables * t , const char * colname )
{
struct columns * column ;
/* Check that the column exists in the table */
AST_LIST_TRAVERSE ( & t -> columns , column , list ) {
if ( strcmp ( column -> name , colname ) == 0 ) {
return column ;
}
}
return NULL ;
}
static struct ast_variable * realtime_pgsql ( const char * database , const char * tablename , va_list ap )
2006-04-05 17:46:09 +00:00
{
PGresult * result = NULL ;
2008-10-14 00:08:52 +00:00
int num_rows = 0 , pgresult ;
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 );
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 100 );
2006-04-05 17:46:09 +00:00
char * stringp ;
char * chunk ;
char * op ;
const char * newparam , * newval ;
2006-04-10 02:01:39 +00:00
struct ast_variable * var = NULL , * prev = NULL ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
if ( ! tablename ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , "PostgreSQL RealTime: No table specified. \n " );
2006-04-05 17:46:09 +00:00
return NULL ;
}
/* Get the first parameter and first value in our list of passed paramater/value pairs */
newparam = va_arg ( ap , const char * );
newval = va_arg ( ap , const char * );
2006-04-10 02:01:39 +00:00
if ( ! newparam || ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on. \n " );
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
2008-10-14 00:08:52 +00:00
}
2006-04-05 17:46:09 +00:00
return NULL ;
}
/* Create the first part of the query using the first parameter/value pairs we just extracted
If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
2006-04-10 02:01:39 +00:00
op = strchr ( newparam , ' ' ) ? "" : " =" ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
va_end ( ap );
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , "SELECT * FROM %s WHERE %s%s '%s'" , tablename , newparam , op , ast_str_buffer ( escapebuf ));
2006-04-10 02:01:39 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
2006-04-05 17:46:09 +00:00
newval = va_arg ( ap , const char * );
2006-04-10 02:01:39 +00:00
if ( ! strchr ( newparam , ' ' ))
op = " =" ;
else
op = "" ;
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
va_end ( ap );
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " AND %s%s '%s'" , newparam , op , ast_str_buffer ( escapebuf ));
2006-04-05 17:46:09 +00:00
}
va_end ( ap );
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock );
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database )) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
2006-04-10 02:01:39 +00:00
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql )))) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
"PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info. \n " , tablename , database );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
2006-04-10 02:01:39 +00:00
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
"PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info. \n " , tablename , database );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Result=%p Query: %s \n " , result , ast_str_buffer ( sql ));
2006-04-05 17:46:09 +00:00
2006-04-10 02:01:39 +00:00
if (( num_rows = PQntuples ( result )) > 0 ) {
int i = 0 ;
int rowIndex = 0 ;
int numFields = PQnfields ( result );
char ** fieldnames = NULL ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Found %d rows. \n " , num_rows );
2006-04-10 02:01:39 +00:00
2006-04-10 02:15:47 +00:00
if ( ! ( fieldnames = ast_calloc ( 1 , numFields * sizeof ( char * )))) {
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock );
PQclear ( result );
return NULL ;
}
for ( i = 0 ; i < numFields ; i ++ )
fieldnames [ i ] = PQfname ( result , i );
for ( rowIndex = 0 ; rowIndex < num_rows ; rowIndex ++ ) {
for ( i = 0 ; i < numFields ; i ++ ) {
stringp = PQgetvalue ( result , rowIndex , i );
while ( stringp ) {
chunk = strsep ( & stringp , ";" );
2007-06-13 17:37:06 +00:00
if ( ! ast_strlen_zero ( ast_strip ( chunk ))) {
2006-04-10 02:01:39 +00:00
if ( prev ) {
2007-08-29 20:55:40 +00:00
prev -> next = ast_variable_new ( fieldnames [ i ], chunk , "" );
2006-04-10 02:01:39 +00:00
if ( prev -> next ) {
prev = prev -> next ;
}
} else {
2007-08-29 20:55:40 +00:00
prev = var = ast_variable_new ( fieldnames [ i ], chunk , "" );
2006-04-10 02:01:39 +00:00
}
}
}
}
2006-04-05 17:46:09 +00:00
}
2007-06-06 21:20:11 +00:00
ast_free ( fieldnames );
2006-04-10 02:01:39 +00:00
} else {
2008-10-14 00:08:52 +00:00
ast_debug ( 1 , "Postgresql RealTime: Could not find any rows in table %s@%s. \n " , tablename , database );
2006-04-05 17:46:09 +00:00
}
ast_mutex_unlock ( & pgsql_lock );
PQclear ( result );
return var ;
}
static struct ast_config * realtime_multi_pgsql ( const char * database , const char * table , va_list ap )
{
PGresult * result = NULL ;
2008-10-14 00:08:52 +00:00
int num_rows = 0 , pgresult ;
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 );
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 100 );
2006-04-05 17:46:09 +00:00
const char * initfield = NULL ;
char * stringp ;
char * chunk ;
char * op ;
const char * newparam , * newval ;
2006-04-10 02:01:39 +00:00
struct ast_variable * var = NULL ;
2006-04-05 17:46:09 +00:00
struct ast_config * cfg = NULL ;
struct ast_category * cat = NULL ;
2006-04-10 02:01:39 +00:00
if ( ! table ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , "PostgreSQL RealTime: No table specified. \n " );
2006-04-05 17:46:09 +00:00
return NULL ;
}
2006-04-10 02:01:39 +00:00
2006-04-10 02:15:47 +00:00
if ( ! ( cfg = ast_config_new ()))
2006-04-05 17:46:09 +00:00
return NULL ;
/* Get the first parameter and first value in our list of passed paramater/value pairs */
newparam = va_arg ( ap , const char * );
newval = va_arg ( ap , const char * );
2006-04-10 02:01:39 +00:00
if ( ! newparam || ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on. \n " );
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
2008-10-14 00:08:52 +00:00
}
2006-04-05 17:46:09 +00:00
return NULL ;
}
initfield = ast_strdupa ( newparam );
2006-05-10 13:22:15 +00:00
if (( op = strchr ( initfield , ' ' ))) {
2006-04-05 17:46:09 +00:00
* op = '\0' ;
}
/* Create the first part of the query using the first parameter/value pairs we just extracted
If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
2006-04-10 02:01:39 +00:00
if ( ! strchr ( newparam , ' ' ))
op = " =" ;
else
op = "" ;
2006-04-05 17:46:09 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
va_end ( ap );
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , "SELECT * FROM %s WHERE %s%s '%s'" , table , newparam , op , ast_str_buffer ( escapebuf ));
2006-04-10 02:01:39 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
2006-04-05 17:46:09 +00:00
newval = va_arg ( ap , const char * );
2006-04-10 02:01:39 +00:00
if ( ! strchr ( newparam , ' ' ))
op = " =" ;
else
op = "" ;
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
va_end ( ap );
return NULL ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " AND %s%s '%s'" , newparam , op , ast_str_buffer ( escapebuf ));
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
if ( initfield ) {
2008-10-14 00:08:52 +00:00
ast_str_append ( & sql , 0 , " ORDER BY %s" , initfield );
2006-04-05 17:46:09 +00:00
}
va_end ( ap );
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock );
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database )) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql )))) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
"PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info. \n " , table , database );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
2006-04-10 02:01:39 +00:00
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
"PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info. \n " , table , database );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
2006-04-05 17:46:09 +00:00
}
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Result=%p Query: %s \n " , result , ast_str_buffer ( sql ));
2006-04-05 17:46:09 +00:00
2006-04-10 02:01:39 +00:00
if (( num_rows = PQntuples ( result )) > 0 ) {
int numFields = PQnfields ( result );
int i = 0 ;
int rowIndex = 0 ;
char ** fieldnames = NULL ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Found %d rows. \n " , num_rows );
2006-04-10 02:01:39 +00:00
2006-04-10 02:15:47 +00:00
if ( ! ( fieldnames = ast_calloc ( 1 , numFields * sizeof ( char * )))) {
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock );
PQclear ( result );
return NULL ;
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
for ( i = 0 ; i < numFields ; i ++ )
fieldnames [ i ] = PQfname ( result , i );
for ( rowIndex = 0 ; rowIndex < num_rows ; rowIndex ++ ) {
var = NULL ;
2007-08-29 20:55:40 +00:00
if ( ! ( cat = ast_category_new ( "" , "" , 99999 )))
2006-04-10 02:01:39 +00:00
continue ;
for ( i = 0 ; i < numFields ; i ++ ) {
stringp = PQgetvalue ( result , rowIndex , i );
while ( stringp ) {
chunk = strsep ( & stringp , ";" );
2007-06-13 17:37:06 +00:00
if ( ! ast_strlen_zero ( ast_strip ( chunk ))) {
2006-04-10 02:01:39 +00:00
if ( initfield && ! strcmp ( initfield , fieldnames [ i ])) {
ast_category_rename ( cat , chunk );
}
2007-08-29 20:55:40 +00:00
var = ast_variable_new ( fieldnames [ i ], chunk , "" );
2006-04-10 02:01:39 +00:00
ast_variable_append ( cat , var );
}
}
}
ast_category_append ( cfg , cat );
}
2007-06-06 21:20:11 +00:00
ast_free ( fieldnames );
2006-04-05 17:46:09 +00:00
} else {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Could not find any rows in table %s. \n " , table );
2006-04-05 17:46:09 +00:00
}
ast_mutex_unlock ( & pgsql_lock );
PQclear ( result );
return cfg ;
}
2008-06-05 19:07:27 +00:00
static int update_pgsql ( const char * database , const char * tablename , const char * keyfield ,
2006-04-10 02:01:39 +00:00
const char * lookup , va_list ap )
2006-04-05 17:46:09 +00:00
{
2006-04-10 02:01:39 +00:00
PGresult * result = NULL ;
2008-10-14 00:08:52 +00:00
int numrows = 0 , pgresult ;
2006-04-05 17:46:09 +00:00
const char * newparam , * newval ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 );
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 100 );
2008-06-05 19:07:27 +00:00
struct tables * table ;
struct columns * column = NULL ;
2006-04-05 17:46:09 +00:00
2008-06-05 19:07:27 +00:00
if ( ! tablename ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , "PostgreSQL RealTime: No table specified. \n " );
2008-06-05 19:07:27 +00:00
return - 1 ;
}
if ( ! ( table = find_table ( tablename ))) {
ast_log ( LOG_ERROR , "Table '%s' does not exist!! \n " , tablename );
2006-04-10 02:01:39 +00:00
return - 1 ;
2006-04-05 17:46:09 +00:00
}
/* Get the first parameter and first value in our list of passed paramater/value pairs */
newparam = va_arg ( ap , const char * );
newval = va_arg ( ap , const char * );
2006-04-10 02:01:39 +00:00
if ( ! newparam || ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on. \n " );
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
2008-10-14 00:08:52 +00:00
}
release_table ( table );
2008-06-05 19:07:27 +00:00
return - 1 ;
}
/* Check that the column exists in the table */
AST_LIST_TRAVERSE ( & table -> columns , column , list ) {
if ( strcmp ( column -> name , newparam ) == 0 ) {
break ;
}
}
if ( ! column ) {
ast_log ( LOG_ERROR , "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'! \n " , newparam , tablename );
2008-10-14 00:08:52 +00:00
release_table ( table );
2006-04-10 02:01:39 +00:00
return - 1 ;
2006-04-05 17:46:09 +00:00
}
/* Create the first part of the query using the first parameter/value pairs we just extracted
If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
va_end ( ap );
2008-10-14 00:08:52 +00:00
release_table ( table );
2007-11-29 19:35:49 +00:00
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , "UPDATE %s SET %s = '%s'" , tablename , newparam , ast_str_buffer ( escapebuf ));
2007-11-29 19:35:49 +00:00
2006-04-10 02:01:39 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
2006-04-05 17:46:09 +00:00
newval = va_arg ( ap , const char * );
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
if ( ! find_column ( table , newparam )) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_WARNING , "Attempted to update column '%s' in table '%s', but column does not exist! \n " , newparam , tablename );
continue ;
}
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
va_end ( ap );
2008-10-14 00:08:52 +00:00
release_table ( table );
2007-11-29 19:35:49 +00:00
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , ", %s = '%s'" , newparam , ast_str_buffer ( escapebuf ));
2006-04-05 17:46:09 +00:00
}
va_end ( ap );
2008-10-14 00:08:52 +00:00
release_table ( table );
2007-11-29 19:35:49 +00:00
2008-10-14 00:08:52 +00:00
ESCAPE_STRING ( escapebuf , lookup );
if ( pgresult ) {
2007-11-29 19:35:49 +00:00
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , lookup );
va_end ( ap );
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " WHERE %s = '%s'" , keyfield , ast_str_buffer ( escapebuf ));
2006-04-05 17:46:09 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Update SQL: %s \n " , ast_str_buffer ( sql ));
2006-04-05 17:46:09 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock );
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database )) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql )))) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
2006-04-10 02:01:39 +00:00
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
2006-04-05 17:46:09 +00:00
}
numrows = atoi ( PQcmdTuples ( result ));
ast_mutex_unlock ( & pgsql_lock );
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Updated %d rows on table: %s \n " , numrows , tablename );
2006-04-05 17:46:09 +00:00
/* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
* An integer greater than zero indicates the number of rows affected
* Zero indicates that no records were updated
* -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
2006-04-10 02:01:39 +00:00
*/
2006-04-05 17:46:09 +00:00
2006-04-10 02:01:39 +00:00
if ( numrows >= 0 )
return ( int ) numrows ;
2006-04-05 17:46:09 +00:00
return - 1 ;
}
2008-10-14 00:08:52 +00:00
static int update2_pgsql ( const char * database , const char * tablename , va_list ap )
{
PGresult * result = NULL ;
int numrows = 0 , pgresult , first = 1 ;
struct ast_str * escapebuf = ast_str_thread_get ( & escapebuf_buf , 16 );
const char * newparam , * newval ;
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 );
struct ast_str * where = ast_str_thread_get ( & where_buf , 100 );
struct tables * table ;
if ( ! tablename ) {
ast_log ( LOG_WARNING , "PostgreSQL RealTime: No table specified. \n " );
return - 1 ;
}
if ( ! escapebuf || ! sql || ! where ) {
/* Memory error, already handled */
return - 1 ;
}
if ( ! ( table = find_table ( tablename ))) {
ast_log ( LOG_ERROR , "Table '%s' does not exist!! \n " , tablename );
return - 1 ;
}
ast_str_set ( & sql , 0 , "UPDATE %s SET " , tablename );
ast_str_set ( & where , 0 , "WHERE" );
while (( newparam = va_arg ( ap , const char * ))) {
if ( ! find_column ( table , newparam )) {
ast_log ( LOG_ERROR , "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist! \n " , newparam , tablename , database );
release_table ( table );
return - 1 ;
}
newval = va_arg ( ap , const char * );
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
release_table ( table );
ast_free ( sql );
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & where , 0 , "%s %s='%s'" , first ? "" : " AND" , newparam , ast_str_buffer ( escapebuf ));
2008-10-14 00:08:52 +00:00
first = 0 ;
}
if ( first ) {
ast_log ( LOG_WARNING ,
"PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on. \n " );
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
}
release_table ( table );
return - 1 ;
}
/* Now retrieve the columns to update */
first = 1 ;
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
/* If the column is not within the table, then skip it */
if ( ! find_column ( table , newparam )) {
ast_log ( LOG_NOTICE , "Attempted to update column '%s' in table '%s@%s', but column does not exist! \n " , newparam , tablename , database );
continue ;
}
ESCAPE_STRING ( escapebuf , newval );
if ( pgresult ) {
ast_log ( LOG_ERROR , "Postgres detected invalid input: '%s' \n " , newval );
release_table ( table );
ast_free ( sql );
return - 1 ;
}
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , "%s %s='%s'" , first ? "" : "," , newparam , ast_str_buffer ( escapebuf ));
2008-10-14 00:08:52 +00:00
}
release_table ( table );
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " %s" , ast_str_buffer ( where ));
2008-10-14 00:08:52 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Update SQL: %s \n " , ast_str_buffer ( sql ));
2008-10-14 00:08:52 +00:00
/* We now have our complete statement; connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock );
if ( ! pgsql_reconnect ( database )) {
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql )))) {
2008-10-14 00:08:52 +00:00
ast_log ( LOG_WARNING ,
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2008-10-14 00:08:52 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2008-10-14 00:08:52 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
}
numrows = atoi ( PQcmdTuples ( result ));
ast_mutex_unlock ( & pgsql_lock );
ast_debug ( 1 , "PostgreSQL RealTime: Updated %d rows on table: %s \n " , numrows , tablename );
/* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
* An integer greater than zero indicates the number of rows affected
* Zero indicates that no records were updated
* -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
*/
if ( numrows >= 0 ) {
return ( int ) numrows ;
}
return - 1 ;
}
2008-09-28 21:39:07 +00:00
2007-08-17 13:40:11 +00:00
static int store_pgsql ( const char * database , const char * table , va_list ap )
{
PGresult * result = NULL ;
Oid insertid ;
2008-10-14 00:08:52 +00:00
struct ast_str * buf = ast_str_thread_get ( & escapebuf_buf , 256 );
struct ast_str * sql1 = ast_str_thread_get ( & sql_buf , 256 );
struct ast_str * sql2 = ast_str_thread_get ( & where_buf , 256 );
2007-08-17 13:40:11 +00:00
int pgresult ;
const char * newparam , * newval ;
if ( ! table ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , "PostgreSQL RealTime: No table specified. \n " );
2007-08-17 13:40:11 +00:00
return - 1 ;
}
/* Get the first parameter and first value in our list of passed paramater/value pairs */
newparam = va_arg ( ap , const char * );
newval = va_arg ( ap , const char * );
if ( ! newparam || ! newval ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store. \n " );
2007-08-17 13:40:11 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
2008-09-28 21:39:07 +00:00
}
2007-08-17 13:40:11 +00:00
return - 1 ;
}
/* Must connect to the server before anything else, as the escape function requires the connection handle.. */
ast_mutex_lock ( & pgsql_lock );
if ( ! pgsql_reconnect ( database )) {
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
/* Create the first part of the query using the first parameter/value pairs we just extracted
If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newparam );
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql1 , 0 , "INSERT INTO %s (%s" , table , ast_str_buffer ( buf ));
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newval );
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql2 , 0 , ") VALUES ('%s'" , ast_str_buffer ( buf ));
2007-08-17 13:40:11 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newparam );
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql1 , 0 , ", %s" , ast_str_buffer ( buf ));
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf , newval );
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql2 , 0 , ", '%s'" , ast_str_buffer ( buf ));
2007-08-17 13:40:11 +00:00
}
va_end ( ap );
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql1 , 0 , "%s)" , ast_str_buffer ( sql2 ));
2007-08-17 13:40:11 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Insert SQL: %s \n " , ast_str_buffer ( sql1 ));
2007-08-17 13:40:11 +00:00
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql1 )))) {
2007-08-17 13:40:11 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql1 ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
2007-08-17 13:40:11 +00:00
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql1 ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2007-08-17 13:40:11 +00:00
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
}
insertid = PQoidValue ( result );
ast_mutex_unlock ( & pgsql_lock );
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: row inserted on table: %s, id: %u \n " , table , insertid );
2007-08-17 13:40:11 +00:00
/* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
* An integer greater than zero indicates the number of rows affected
* Zero indicates that no records were updated
* -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
*/
if ( insertid >= 0 )
return ( int ) insertid ;
return - 1 ;
}
static int destroy_pgsql ( const char * database , const char * table , const char * keyfield , const char * lookup , va_list ap )
{
PGresult * result = NULL ;
int numrows = 0 ;
int pgresult ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 256 );
struct ast_str * buf1 = ast_str_thread_get ( & where_buf , 60 ), * buf2 = ast_str_thread_get ( & escapebuf_buf , 60 );
2007-08-17 13:40:11 +00:00
const char * newparam , * newval ;
if ( ! table ) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , "PostgreSQL RealTime: No table specified. \n " );
2007-08-17 13:40:11 +00:00
return - 1 ;
}
/* Get the first parameter and first value in our list of passed paramater/value pairs */
/*newparam = va_arg(ap, const char *);
newval = va_arg(ap, const char *);
if (!newparam || !newval) {*/
if ( ast_strlen_zero ( keyfield ) || ast_strlen_zero ( lookup )) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on. \n " );
2007-08-17 13:40:11 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
};
return - 1 ;
}
/* Must connect to the server before anything else, as the escape function requires the connection handle.. */
ast_mutex_lock ( & pgsql_lock );
if ( ! pgsql_reconnect ( database )) {
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
/* Create the first part of the query using the first parameter/value pairs we just extracted
If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf1 , keyfield );
ESCAPE_STRING ( buf2 , lookup );
2008-12-13 08:36:35 +00:00
ast_str_set ( & sql , 0 , "DELETE FROM %s WHERE %s = '%s'" , table , ast_str_buffer ( buf1 ), ast_str_buffer ( buf2 ));
2007-08-17 13:40:11 +00:00
while (( newparam = va_arg ( ap , const char * ))) {
newval = va_arg ( ap , const char * );
2008-09-28 21:39:07 +00:00
ESCAPE_STRING ( buf1 , newparam );
ESCAPE_STRING ( buf2 , newval );
2008-12-13 08:36:35 +00:00
ast_str_append ( & sql , 0 , " AND %s = '%s'" , ast_str_buffer ( buf1 ), ast_str_buffer ( buf2 ));
2007-08-17 13:40:11 +00:00
}
va_end ( ap );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Delete SQL: %s \n " , ast_str_buffer ( sql ));
2007-08-17 13:40:11 +00:00
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql )))) {
2007-08-17 13:40:11 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
2007-08-17 13:40:11 +00:00
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2007-08-17 13:40:11 +00:00
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
ast_mutex_unlock ( & pgsql_lock );
return - 1 ;
}
}
numrows = atoi ( PQcmdTuples ( result ));
ast_mutex_unlock ( & pgsql_lock );
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Deleted %d rows on table: %s \n " , numrows , table );
2007-08-17 13:40:11 +00:00
/* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
* An integer greater than zero indicates the number of rows affected
* Zero indicates that no records were updated
* -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
*/
if ( numrows >= 0 )
return ( int ) numrows ;
return - 1 ;
}
2006-04-10 02:01:39 +00:00
static struct ast_config * config_pgsql ( const char * database , const char * table ,
2007-08-29 20:55:40 +00:00
const char * file , struct ast_config * cfg ,
2008-03-11 22:55:16 +00:00
struct ast_flags flags , const char * suggested_incl , const char * who_asked )
2006-04-05 17:46:09 +00:00
{
PGresult * result = NULL ;
long num_rows ;
2006-04-06 06:55:38 +00:00
struct ast_variable * new_v ;
struct ast_category * cur_cat = NULL ;
2008-10-14 00:08:52 +00:00
struct ast_str * sql = ast_str_thread_get ( & sql_buf , 100 );
2006-04-05 17:46:09 +00:00
char last [ 80 ] = "" ;
int last_cat_metric = 0 ;
last [ 0 ] = '\0' ;
2006-04-10 02:01:39 +00:00
if ( ! file || ! strcmp ( file , RES_CONFIG_PGSQL_CONF )) {
2007-10-19 18:01:00 +00:00
ast_log ( LOG_WARNING , "PostgreSQL RealTime: Cannot configure myself. \n " );
2006-04-05 17:46:09 +00:00
return NULL ;
}
2008-10-14 00:08:52 +00:00
ast_str_set ( & sql , 0 , "SELECT category, var_name, var_val, cat_metric FROM %s "
"WHERE filename='%s' and commented=0"
"ORDER BY cat_metric DESC, var_metric ASC, category, var_name " , table , file );
2006-04-05 17:46:09 +00:00
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Static SQL: %s \n " , ast_str_buffer ( sql ));
2006-04-05 17:46:09 +00:00
/* We now have our complete statement; Lets connect to the server and execute it. */
ast_mutex_lock ( & pgsql_lock );
2006-04-10 02:01:39 +00:00
if ( ! pgsql_reconnect ( database )) {
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
2008-12-13 08:36:35 +00:00
if ( ! ( result = PQexec ( pgsqlConn , ast_str_buffer ( sql )))) {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2008-10-14 00:08:52 +00:00
"PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info. \n " , table , database );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s \n " , PQerrorMessage ( pgsqlConn ));
2006-04-05 17:46:09 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
2006-04-10 02:01:39 +00:00
} else {
ExecStatusType result_status = PQresultStatus ( result );
if ( result_status != PGRES_COMMAND_OK
&& result_status != PGRES_TUPLES_OK
&& result_status != PGRES_NONFATAL_ERROR ) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Failed to query database. Check debug for more info. \n " );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query: %s \n " , ast_str_buffer ( sql ));
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Query Failed because: %s (%s) \n " ,
2006-10-03 15:53:07 +00:00
PQresultErrorMessage ( result ), PQresStatus ( result_status ));
2006-04-10 02:01:39 +00:00
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
2006-04-05 17:46:09 +00:00
}
2006-04-10 02:01:39 +00:00
if (( num_rows = PQntuples ( result )) > 0 ) {
int rowIndex = 0 ;
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Found %ld rows. \n " , num_rows );
2006-04-10 02:01:39 +00:00
for ( rowIndex = 0 ; rowIndex < num_rows ; rowIndex ++ ) {
char * field_category = PQgetvalue ( result , rowIndex , 0 );
char * field_var_name = PQgetvalue ( result , rowIndex , 1 );
char * field_var_val = PQgetvalue ( result , rowIndex , 2 );
char * field_cat_metric = PQgetvalue ( result , rowIndex , 3 );
if ( ! strcmp ( field_var_name , "#include" )) {
2008-03-11 22:55:16 +00:00
if ( ! ast_config_internal_load ( field_var_val , cfg , flags , "" , who_asked )) {
2006-04-05 17:46:09 +00:00
PQclear ( result );
ast_mutex_unlock ( & pgsql_lock );
return NULL ;
}
continue ;
}
2006-04-10 02:01:39 +00:00
if ( strcmp ( last , field_category ) || last_cat_metric != atoi ( field_cat_metric )) {
2007-08-29 20:55:40 +00:00
cur_cat = ast_category_new ( field_category , "" , 99999 );
2006-04-10 02:19:58 +00:00
if ( ! cur_cat )
2006-04-05 17:46:09 +00:00
break ;
strcpy ( last , field_category );
last_cat_metric = atoi ( field_cat_metric );
ast_category_append ( cfg , cur_cat );
}
2007-08-29 20:55:40 +00:00
new_v = ast_variable_new ( field_var_name , field_var_val , "" );
2006-04-05 17:46:09 +00:00
ast_variable_append ( cur_cat , new_v );
}
} else {
2006-04-10 02:01:39 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Could not find config '%s' in database. \n " , file );
2006-04-05 17:46:09 +00:00
}
PQclear ( result );
ast_mutex_unlock ( & pgsql_lock );
return cfg ;
}
2008-06-05 19:07:27 +00:00
static int require_pgsql ( const char * database , const char * tablename , va_list ap )
{
struct columns * column ;
struct tables * table = find_table ( tablename );
char * elm ;
int type , size , res = 0 ;
if ( ! table ) {
ast_log ( LOG_WARNING , "Table %s not found in database. This table should exist if you're using realtime. \n " , tablename );
return - 1 ;
}
while (( elm = va_arg ( ap , char * ))) {
type = va_arg ( ap , require_type );
size = va_arg ( ap , int );
AST_LIST_TRAVERSE ( & table -> columns , column , list ) {
if ( strcmp ( column -> name , elm ) == 0 ) {
/* Char can hold anything, as long as it is large enough */
if (( strncmp ( column -> type , "char" , 4 ) == 0 || strncmp ( column -> type , "varchar" , 7 ) == 0 || strcmp ( column -> type , "bpchar" ) == 0 )) {
if (( size > column -> len ) && column -> len != - 1 ) {
ast_log ( LOG_WARNING , "Column '%s' should be at least %d long, but is only %d long. \n " , column -> name , size , column -> len );
res = - 1 ;
}
} else if ( strncmp ( column -> type , "int" , 3 ) == 0 ) {
int typesize = atoi ( column -> type + 3 );
/* Integers can hold only other integers */
2008-06-09 22:51:59 +00:00
if (( type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
type == RQ_UINTEGER2 ) && typesize == 2 ) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_WARNING , "Column '%s' may not be large enough for the required data length: %d \n " , column -> name , size );
res = - 1 ;
2008-06-09 22:51:59 +00:00
} else if (( type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
type == RQ_UINTEGER4 ) && typesize == 4 ) {
ast_log ( LOG_WARNING , "Column '%s' may not be large enough for the required data length: %d \n " , column -> name , size );
res = - 1 ;
} else if ( type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE ) {
2008-06-13 21:50:28 +00:00
ast_log ( LOG_WARNING , "Column '%s' is of the incorrect type: (need %s(%d) but saw %s) \n " ,
column -> name ,
type == RQ_CHAR ? "char" :
type == RQ_DATETIME ? "datetime" :
type == RQ_DATE ? "date" :
type == RQ_FLOAT ? "float" :
"a rather stiff drink " ,
size , column -> type );
2008-06-05 19:07:27 +00:00
res = - 1 ;
}
2008-06-09 22:51:59 +00:00
} else if ( strncmp ( column -> type , "float" , 5 ) == 0 && ! ast_rq_is_int ( type ) && type != RQ_FLOAT ) {
2008-06-05 19:07:27 +00:00
ast_log ( LOG_WARNING , "Column %s cannot be a %s \n " , column -> name , column -> type );
res = - 1 ;
} else { /* There are other types that no module implements yet */
ast_log ( LOG_WARNING , "Possibly unsupported column type '%s' on column '%s' \n " , column -> type , column -> name );
res = - 1 ;
}
break ;
}
}
if ( ! column ) {
if ( requirements == RQ_WARN ) {
ast_log ( LOG_WARNING , "Table %s requires a column '%s' of size '%d', but no such column exists. \n " , tablename , elm , size );
} else {
2008-06-09 22:51:59 +00:00
struct ast_str * sql = ast_str_create ( 100 );
char fieldtype [ 15 ];
2008-08-10 00:47:56 +00:00
PGresult * result ;
2008-06-05 19:07:27 +00:00
if ( requirements == RQ_CREATECHAR || type == RQ_CHAR ) {
2008-07-31 20:10:39 +00:00
/* Size is minimum length; make it at least 50% greater,
* just to be sure, because PostgreSQL doesn't support
* resizing columns. */
snprintf ( fieldtype , sizeof ( fieldtype ), "CHAR(%d)" ,
size < 15 ? size * 2 :
( size * 3 / 2 > 255 ) ? 255 : size * 3 / 2 );
2008-06-09 22:51:59 +00:00
} else if ( type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2 ) {
snprintf ( fieldtype , sizeof ( fieldtype ), "INT2" );
} else if ( type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4 ) {
snprintf ( fieldtype , sizeof ( fieldtype ), "INT4" );
} else if ( type == RQ_UINTEGER4 || type == RQ_INTEGER8 ) {
snprintf ( fieldtype , sizeof ( fieldtype ), "INT8" );
} else if ( type == RQ_UINTEGER8 ) {
/* No such type on PostgreSQL */
snprintf ( fieldtype , sizeof ( fieldtype ), "CHAR(20)" );
2008-06-05 19:07:27 +00:00
} else if ( type == RQ_FLOAT ) {
2008-06-09 22:51:59 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ), "FLOAT8" );
2008-06-05 19:07:27 +00:00
} else if ( type == RQ_DATE ) {
2008-06-09 22:51:59 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ), "DATE" );
2008-06-05 19:07:27 +00:00
} else if ( type == RQ_DATETIME ) {
2008-06-09 22:51:59 +00:00
snprintf ( fieldtype , sizeof ( fieldtype ), "TIMESTAMP" );
2008-06-05 19:07:27 +00:00
} else {
2008-07-31 20:10:39 +00:00
ast_log ( LOG_ERROR , "Unrecognized request type %d \n " , type );
2008-06-05 19:07:27 +00:00
ast_free ( sql );
continue ;
}
2008-06-09 22:51:59 +00:00
ast_str_set ( & sql , 0 , "ALTER TABLE %s ADD COLUMN %s %s" , tablename , elm , fieldtype );
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , "About to lock pgsql_lock (running alter on table '%s' to add column '%s') \n " , tablename , elm );
ast_mutex_lock ( & pgsql_lock );
if ( ! pgsql_reconnect ( database )) {
ast_mutex_unlock ( & pgsql_lock );
2008-12-13 08:36:35 +00:00
ast_log ( LOG_ERROR , "Unable to add column: %s \n " , ast_str_buffer ( sql ));
2008-06-05 19:07:27 +00:00
ast_free ( sql );
continue ;
}
ast_debug ( 1 , "About to run ALTER query on table '%s' to add column '%s' \n " , tablename , elm );
2008-12-13 08:36:35 +00:00
result = PQexec ( pgsqlConn , ast_str_buffer ( sql ));
2008-06-05 19:07:27 +00:00
ast_debug ( 1 , "Finished running ALTER query on table '%s' \n " , tablename );
2008-08-10 00:47:56 +00:00
if ( PQresultStatus ( result ) != PGRES_COMMAND_OK ) {
2008-12-13 08:36:35 +00:00
ast_log ( LOG_ERROR , "Unable to add column: %s \n " , ast_str_buffer ( sql ));
2008-06-05 19:07:27 +00:00
}
2008-08-10 00:47:56 +00:00
PQclear ( result );
2008-06-05 19:07:27 +00:00
ast_mutex_unlock ( & pgsql_lock );
ast_free ( sql );
}
}
}
2008-10-14 00:08:52 +00:00
release_table ( table );
2008-06-05 19:07:27 +00:00
return res ;
}
static int unload_pgsql ( const char * database , const char * tablename )
{
struct tables * cur ;
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , "About to lock table cache list \n " );
2008-06-05 19:07:27 +00:00
AST_LIST_LOCK ( & psql_tables );
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , "About to traverse table cache list \n " );
2008-06-05 19:07:27 +00:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( & psql_tables , cur , list ) {
if ( strcmp ( cur -> name , tablename ) == 0 ) {
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , "About to remove matching cache entry \n " );
2008-06-05 19:07:27 +00:00
AST_LIST_REMOVE_CURRENT ( list );
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , "About to destroy matching cache entry \n " );
2008-06-05 19:07:27 +00:00
destroy_table ( cur );
2008-07-31 20:10:39 +00:00
ast_debug ( 1 , "Cache entry '%s@%s' destroyed \n " , tablename , database );
2008-06-05 19:07:27 +00:00
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK ( & psql_tables );
2008-07-31 20:10:39 +00:00
ast_debug ( 2 , "About to return \n " );
2008-06-05 19:07:27 +00:00
return cur ? 0 : - 1 ;
}
2006-04-05 17:46:09 +00:00
static struct ast_config_engine pgsql_engine = {
. name = "pgsql" ,
. load_func = config_pgsql ,
. realtime_func = realtime_pgsql ,
. realtime_multi_func = realtime_multi_pgsql ,
2007-08-17 13:40:11 +00:00
. store_func = store_pgsql ,
. destroy_func = destroy_pgsql ,
2008-06-05 19:07:27 +00:00
. update_func = update_pgsql ,
2008-10-14 00:08:52 +00:00
. update2_func = update2_pgsql ,
2008-06-05 19:07:27 +00:00
. require_func = require_pgsql ,
. unload_func = unload_pgsql ,
2006-04-05 17:46:09 +00:00
};
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2006-04-05 17:46:09 +00:00
{
2007-08-16 21:09:46 +00:00
if ( ! parse_config ( 0 ))
2006-08-31 21:00:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
2006-04-05 17:46:09 +00:00
ast_config_engine_register ( & pgsql_engine );
2007-10-19 18:01:00 +00:00
ast_verb ( 1 , "PostgreSQL RealTime driver loaded. \n " );
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_realtime , ARRAY_LEN ( cli_realtime ));
2006-04-05 17:46:09 +00:00
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2006-04-05 17:46:09 +00:00
{
2008-06-05 19:07:27 +00:00
struct tables * table ;
2007-08-16 21:09:46 +00:00
/* Acquire control before doing anything to the module itself. */
2006-04-05 17:46:09 +00:00
ast_mutex_lock ( & pgsql_lock );
2006-04-10 02:01:39 +00:00
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
2007-08-16 21:09:46 +00:00
}
2008-12-05 10:31:25 +00:00
ast_cli_unregister_multiple ( cli_realtime , ARRAY_LEN ( cli_realtime ));
2006-04-05 17:46:09 +00:00
ast_config_engine_deregister ( & pgsql_engine );
2007-10-19 18:01:00 +00:00
ast_verb ( 1 , "PostgreSQL RealTime unloaded. \n " );
2006-04-05 17:46:09 +00:00
2008-06-05 19:07:27 +00:00
/* Destroy cached table info */
AST_LIST_LOCK ( & psql_tables );
while (( table = AST_LIST_REMOVE_HEAD ( & psql_tables , list ))) {
destroy_table ( table );
}
AST_LIST_UNLOCK ( & psql_tables );
2006-04-05 17:46:09 +00:00
/* Unlock so something else can destroy the lock. */
ast_mutex_unlock ( & pgsql_lock );
return 0 ;
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2006-04-05 17:46:09 +00:00
{
2007-08-16 21:09:46 +00:00
parse_config ( 1 );
2006-04-05 17:46:09 +00:00
return 0 ;
}
2008-08-10 00:47:56 +00:00
static int parse_config ( int is_reload )
2006-04-05 17:46:09 +00:00
{
struct ast_config * config ;
2006-09-20 21:03:37 +00:00
const char * s ;
2008-08-10 00:47:56 +00:00
struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2006-04-05 17:46:09 +00:00
2008-09-12 23:30:03 +00:00
config = ast_config_load ( RES_CONFIG_PGSQL_CONF , config_flags );
if ( config == CONFIG_STATUS_FILEUNCHANGED ) {
2007-08-16 21:09:46 +00:00
return 0 ;
2008-09-12 23:30:03 +00:00
}
2006-04-05 17:46:09 +00:00
2008-09-12 23:30:03 +00:00
if ( config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID ) {
2007-08-16 21:09:46 +00:00
ast_log ( LOG_WARNING , "Unable to load config %s \n " , RES_CONFIG_PGSQL_CONF );
2006-08-31 21:00:20 +00:00
return 0 ;
}
2007-08-16 21:09:46 +00:00
ast_mutex_lock ( & pgsql_lock );
if ( pgsqlConn ) {
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
}
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , "general" , "dbuser" ))) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: No database user found, using 'asterisk' as default. \n " );
2006-08-31 21:00:20 +00:00
strcpy ( dbuser , "asterisk" );
} else {
ast_copy_string ( dbuser , s , sizeof ( dbuser ));
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , "general" , "dbpass" ))) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: No database password found, using 'asterisk' as default. \n " );
2006-08-31 21:00:20 +00:00
strcpy ( dbpass , "asterisk" );
} else {
ast_copy_string ( dbpass , s , sizeof ( dbpass ));
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , "general" , "dbhost" ))) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: No database host found, using localhost via socket. \n " );
2006-08-31 21:00:20 +00:00
dbhost [ 0 ] = '\0' ;
} else {
ast_copy_string ( dbhost , s , sizeof ( dbhost ));
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , "general" , "dbname" ))) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: No database name found, using 'asterisk' as default. \n " );
2006-08-31 21:00:20 +00:00
strcpy ( dbname , "asterisk" );
} else {
ast_copy_string ( dbname , s , sizeof ( dbname ));
}
2006-04-05 17:46:09 +00:00
2006-08-31 21:00:20 +00:00
if ( ! ( s = ast_variable_retrieve ( config , "general" , "dbport" ))) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: No database port found, using 5432 as default. \n " );
2006-08-31 21:00:20 +00:00
dbport = 5432 ;
} else {
dbport = atoi ( s );
}
2006-04-05 17:46:09 +00:00
2007-12-03 23:29:57 +00:00
if ( ! ast_strlen_zero ( dbhost )) {
/* No socket needed */
} else if ( ! ( s = ast_variable_retrieve ( config , "general" , "dbsock" ))) {
2006-08-31 21:00:20 +00:00
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: No database socket found, using '/tmp/pgsql.sock' as default. \n " );
2006-08-31 21:00:20 +00:00
strcpy ( dbsock , "/tmp/pgsql.sock" );
} else {
ast_copy_string ( dbsock , s , sizeof ( dbsock ));
2006-04-05 17:46:09 +00:00
}
2008-06-05 19:07:27 +00:00
if ( ! ( s = ast_variable_retrieve ( config , "general" , "requirements" ))) {
ast_log ( LOG_WARNING ,
"PostgreSQL RealTime: no requirements setting found, using 'warn' as default. \n " );
requirements = RQ_WARN ;
} else if ( ! strcasecmp ( s , "createclose" )) {
requirements = RQ_CREATECLOSE ;
} else if ( ! strcasecmp ( s , "createchar" )) {
requirements = RQ_CREATECHAR ;
}
2006-04-05 17:46:09 +00:00
ast_config_destroy ( config );
2006-10-03 15:53:07 +00:00
if ( option_debug ) {
2007-09-21 14:40:10 +00:00
if ( ! ast_strlen_zero ( dbhost )) {
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime Host: %s \n " , dbhost );
ast_debug ( 1 , "PostgreSQL RealTime Port: %i \n " , dbport );
2006-10-03 15:53:07 +00:00
} else {
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime Socket: %s \n " , dbsock );
2006-10-03 15:53:07 +00:00
}
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime User: %s \n " , dbuser );
ast_debug ( 1 , "PostgreSQL RealTime Password: %s \n " , dbpass );
ast_debug ( 1 , "PostgreSQL RealTime DBName: %s \n " , dbname );
2006-04-05 17:46:09 +00:00
}
2007-08-16 21:09:46 +00:00
if ( ! pgsql_reconnect ( NULL )) {
ast_log ( LOG_WARNING ,
2007-10-19 18:01:00 +00:00
"PostgreSQL RealTime: Couldn't establish connection. Check debug. \n " );
ast_debug ( 1 , "PostgreSQL RealTime: Cannot Connect: %s \n " , PQerrorMessage ( pgsqlConn ));
2007-08-16 21:09:46 +00:00
}
2007-10-19 18:01:00 +00:00
ast_verb ( 2 , "PostgreSQL RealTime reloaded. \n " );
2007-08-16 21:09:46 +00:00
/* Done reloading. Release lock so others can now use driver. */
ast_mutex_unlock ( & pgsql_lock );
2006-04-05 17:46:09 +00:00
return 1 ;
}
static int pgsql_reconnect ( const char * database )
{
char my_database [ 50 ];
2006-04-10 02:01:39 +00:00
ast_copy_string ( my_database , S_OR ( database , dbname ), sizeof ( my_database ));
2006-04-05 17:46:09 +00:00
/* mutex lock should have been locked before calling this function. */
if ( pgsqlConn && PQstatus ( pgsqlConn ) != CONNECTION_OK ) {
2006-04-10 02:01:39 +00:00
PQfinish ( pgsqlConn );
pgsqlConn = NULL ;
}
2008-02-22 22:39:21 +00:00
/* DB password can legitimately be 0-length */
2008-02-25 15:12:48 +00:00
if (( ! pgsqlConn ) && ( ! ast_strlen_zero ( dbhost ) || ! ast_strlen_zero ( dbsock )) && ! ast_strlen_zero ( dbuser ) && ! ast_strlen_zero ( my_database )) {
2008-02-22 22:39:21 +00:00
struct ast_str * connInfo = ast_str_create ( 32 );
ast_str_set ( & connInfo , 0 , "host=%s port=%d dbname=%s user=%s" ,
dbhost , dbport , my_database , dbuser );
if ( ! ast_strlen_zero ( dbpass ))
ast_str_append ( & connInfo , 0 , " password=%s" , dbpass );
2008-12-13 08:36:35 +00:00
ast_debug ( 1 , "%u connInfo=%s \n " , ( unsigned int ) ast_str_size ( connInfo ), ast_str_buffer ( connInfo ));
pgsqlConn = PQconnectdb ( ast_str_buffer ( connInfo ));
ast_debug ( 1 , "%u connInfo=%s \n " , ( unsigned int ) ast_str_size ( connInfo ), ast_str_buffer ( connInfo ));
2007-06-06 21:20:11 +00:00
ast_free ( connInfo );
2006-04-10 02:15:47 +00:00
connInfo = NULL ;
2008-02-22 22:39:21 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "pgsqlConn=%p \n " , pgsqlConn );
2006-09-18 15:15:33 +00:00
if ( pgsqlConn && PQstatus ( pgsqlConn ) == CONNECTION_OK ) {
2007-10-19 18:01:00 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: Successfully connected to database. \n " );
2006-04-10 02:15:47 +00:00
connect_time = time ( NULL );
2009-10-06 19:31:39 +00:00
version = PQserverVersion ( pgsqlConn );
2006-04-10 02:15:47 +00:00
return 1 ;
} else {
ast_log ( LOG_ERROR ,
2008-02-22 22:39:21 +00:00
"PostgreSQL RealTime: Failed to connect database %s on %s: %s \n " ,
dbname , dbhost , PQresultErrorMessage ( NULL ));
2006-04-10 02:15:47 +00:00
return 0 ;
2006-04-10 02:01:39 +00:00
}
2006-04-05 17:46:09 +00:00
} else {
2008-02-22 22:39:21 +00:00
ast_debug ( 1 , "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks. \n " );
2006-04-10 02:01:39 +00:00
return 1 ;
2006-04-05 17:46:09 +00:00
}
}
2008-06-05 19:07:27 +00:00
static char * handle_cli_realtime_pgsql_cache ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
struct tables * cur ;
int l , which ;
char * ret = NULL ;
switch ( cmd ) {
case CLI_INIT :
2008-09-28 23:32:14 +00:00
e -> command = "realtime show pgsql cache" ;
2008-06-05 19:07:27 +00:00
e -> usage =
2008-09-28 23:32:14 +00:00
"Usage: realtime show pgsql cache [<table>] \n "
2008-06-05 19:07:27 +00:00
" Shows table cache for the PostgreSQL RealTime driver \n " ;
return NULL ;
case CLI_GENERATE :
2008-09-28 23:32:14 +00:00
if ( a -> argc != 4 ) {
2008-06-05 19:07:27 +00:00
return NULL ;
}
l = strlen ( a -> word );
which = 0 ;
AST_LIST_LOCK ( & psql_tables );
AST_LIST_TRAVERSE ( & psql_tables , cur , list ) {
if ( ! strncasecmp ( a -> word , cur -> name , l ) && ++ which > a -> n ) {
ret = ast_strdup ( cur -> name );
break ;
}
}
AST_LIST_UNLOCK ( & psql_tables );
return ret ;
}
2008-09-28 23:32:14 +00:00
if ( a -> argc == 4 ) {
2008-06-05 19:07:27 +00:00
/* List of tables */
AST_LIST_LOCK ( & psql_tables );
AST_LIST_TRAVERSE ( & psql_tables , cur , list ) {
ast_cli ( a -> fd , "%s \n " , cur -> name );
}
AST_LIST_UNLOCK ( & psql_tables );
2008-09-28 23:32:14 +00:00
} else if ( a -> argc == 5 ) {
2008-06-05 19:07:27 +00:00
/* List of columns */
2008-09-28 23:32:14 +00:00
if (( cur = find_table ( a -> argv [ 4 ]))) {
2008-06-05 19:07:27 +00:00
struct columns * col ;
2008-09-28 23:32:14 +00:00
ast_cli ( a -> fd , "Columns for Table Cache '%s': \n " , a -> argv [ 4 ]);
2008-06-05 19:07:27 +00:00
ast_cli ( a -> fd , "%-20.20s %-20.20s %-3.3s %-8.8s \n " , "Name" , "Type" , "Len" , "Nullable" );
AST_LIST_TRAVERSE ( & cur -> columns , col , list ) {
ast_cli ( a -> fd , "%-20.20s %-20.20s %3d %-8.8s \n " , col -> name , col -> type , col -> len , col -> notnull ? "NOT NULL" : "" );
}
2008-10-14 00:08:52 +00:00
release_table ( cur );
2008-06-05 19:07:27 +00:00
} else {
2008-09-28 23:32:14 +00:00
ast_cli ( a -> fd , "No such table '%s' \n " , a -> argv [ 4 ]);
2008-06-05 19:07:27 +00:00
}
}
return 0 ;
}
2007-10-19 18:01:00 +00:00
static char * handle_cli_realtime_pgsql_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2006-04-05 17:46:09 +00:00
{
2008-07-11 00:55:06 +00:00
char status [ 256 ], credentials [ 100 ] = "" ;
2008-08-10 00:47:56 +00:00
int ctimesec = time ( NULL ) - connect_time ;
2006-04-05 17:46:09 +00:00
2007-10-19 18:01:00 +00:00
switch ( cmd ) {
case CLI_INIT :
2008-09-28 23:32:14 +00:00
e -> command = "realtime show pgsql status" ;
2007-10-19 18:01:00 +00:00
e -> usage =
2008-09-28 23:32:14 +00:00
"Usage: realtime show pgsql status \n "
2007-10-19 18:01:00 +00:00
" Shows connection information for the PostgreSQL RealTime driver \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
2008-09-28 23:32:14 +00:00
if ( a -> argc != 4 )
2007-10-19 18:01:00 +00:00
return CLI_SHOWUSAGE ;
2006-04-10 02:01:39 +00:00
if ( pgsqlConn && PQstatus ( pgsqlConn ) == CONNECTION_OK ) {
2007-10-19 18:01:00 +00:00
if ( ! ast_strlen_zero ( dbhost ))
2008-07-11 00:55:06 +00:00
snprintf ( status , sizeof ( status ), "Connected to %s@%s, port %d" , dbname , dbhost , dbport );
2007-10-19 18:01:00 +00:00
else if ( ! ast_strlen_zero ( dbsock ))
2008-07-11 00:55:06 +00:00
snprintf ( status , sizeof ( status ), "Connected to %s on socket file %s" , dbname , dbsock );
2007-10-19 18:01:00 +00:00
else
2008-07-11 00:55:06 +00:00
snprintf ( status , sizeof ( status ), "Connected to %s@%s" , dbname , dbhost );
2006-04-05 17:46:09 +00:00
2007-10-19 18:01:00 +00:00
if ( ! ast_strlen_zero ( dbuser ))
2008-07-11 00:55:06 +00:00
snprintf ( credentials , sizeof ( credentials ), " with username %s" , dbuser );
2006-04-05 17:46:09 +00:00
2008-08-10 00:47:56 +00:00
if ( ctimesec > 31536000 )
2007-10-19 18:01:00 +00:00
ast_cli ( a -> fd , "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds. \n " ,
2008-08-10 00:47:56 +00:00
status , credentials , ctimesec / 31536000 , ( ctimesec % 31536000 ) / 86400 ,
( ctimesec % 86400 ) / 3600 , ( ctimesec % 3600 ) / 60 , ctimesec % 60 );
else if ( ctimesec > 86400 )
2007-10-19 18:01:00 +00:00
ast_cli ( a -> fd , "%s%s for %d days, %d hours, %d minutes, %d seconds. \n " , status ,
2008-08-10 00:47:56 +00:00
credentials , ctimesec / 86400 , ( ctimesec % 86400 ) / 3600 , ( ctimesec % 3600 ) / 60 ,
ctimesec % 60 );
else if ( ctimesec > 3600 )
2008-07-11 00:55:06 +00:00
ast_cli ( a -> fd , "%s%s for %d hours, %d minutes, %d seconds. \n " , status , credentials ,
2008-08-10 00:47:56 +00:00
ctimesec / 3600 , ( ctimesec % 3600 ) / 60 , ctimesec % 60 );
else if ( ctimesec > 60 )
ast_cli ( a -> fd , "%s%s for %d minutes, %d seconds. \n " , status , credentials , ctimesec / 60 ,
ctimesec % 60 );
2007-10-19 18:01:00 +00:00
else
2008-08-10 00:47:56 +00:00
ast_cli ( a -> fd , "%s%s for %d seconds. \n " , status , credentials , ctimesec );
2006-04-05 17:46:09 +00:00
2007-10-19 18:01:00 +00:00
return CLI_SUCCESS ;
2006-04-05 17:46:09 +00:00
} else {
2007-10-19 18:01:00 +00:00
return CLI_FAILURE ;
2006-04-05 17:46:09 +00:00
}
}
2006-08-11 15:05:19 +00:00
2006-08-21 02:11:39 +00:00
/* needs usecount semantics defined */
2009-03-18 02:21:23 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , "PostgreSQL RealTime Configuration Driver" ,
2006-08-21 02:11:39 +00:00
. load = load_module ,
. unload = unload_module ,
. reload = reload
);