/* * Asterisk -- A telephony toolkit for Linux. * * Copyright (C) 1999-2005, Digium, Inc. * * Manuel Guesdon - Postgresql RealTime Driver Author/Adaptor * Mark Spencer - Asterisk Author * Matthew Boehm - MySQL RealTime Driver Author * * res_config_pgsql.c * * v1.0 - (07-11-05) - Initial version based on res_config_mysql v2.0 */ /*! \file * * \brief Postgresql plugin for Asterisk RealTime Architecture * * \author Mark Spencer * \author Manuel Guesdon - Postgresql RealTime Driver Author/Adaptor * * \arg http://www.postgresql.org */ #include #include #include /* PostgreSQL */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/config.h" #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/options.h" #include "asterisk/utils.h" #include "asterisk/cli.h" static char *res_config_pgsql_desc = "Postgresql RealTime Configuration Driver"; AST_MUTEX_DEFINE_STATIC(pgsql_lock); #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf" PGconn* pgsqlConn = NULL; static char dbhost[50]=""; static char dbuser[50]=""; static char dbpass[50]=""; static char dbname[50]=""; static char dbsock[50]=""; static int dbport=5432; static time_t connect_time=0; static int parse_config(void); static int pgsql_reconnect(const char *database); static int realtime_pgsql_status(int fd, int argc, char **argv); STANDARD_LOCAL_USER; LOCAL_USER_DECL; static char cli_realtime_pgsql_status_usage[] = "Usage: realtime pgsql status\n" " Shows connection information for the Postgresql RealTime driver\n"; static struct ast_cli_entry cli_realtime_pgsql_status = { { "realtime", "pgsql", "status", NULL }, realtime_pgsql_status, "Shows connection information for the Postgresql RealTime driver", cli_realtime_pgsql_status_usage, NULL }; static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap) { PGresult *result = NULL; int num_rows = 0; char sql[256]; char *stringp; char *chunk; char *op; const char *newparam, *newval; struct ast_variable *var=NULL, *prev=NULL; if(!table) { ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n"); 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 *); if(!newparam || !newval) { ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn=NULL; }; 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 */ if(!strchr(newparam, ' ')) op = " ="; else op = ""; snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval); while((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if(!strchr(newparam, ' ')) op = " ="; else op = ""; snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval); } va_end(ap); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if(!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return NULL; } if(!(result=PQexec(pgsqlConn, sql))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; } 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"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result),PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return NULL; } } ast_log(LOG_DEBUG, "1Postgresql RealTime: Result=%p Query: %s\n", result, sql); if((num_rows=PQntuples(result))>0) { int i = 0; int rowIndex=0; int numFields = PQnfields(result); char** fieldnames=NULL; ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows); fieldnames=malloc(numFields*sizeof(char*)); if (!fieldnames) { /* If I can't alloc memory at this point, why bother doing anything else? */ ast_log(LOG_WARNING, "Out of memory!\n"); ast_mutex_unlock(&pgsql_lock); PQclear(result); return NULL; } for(i = 0; i < numFields; i++) fieldnames[i]=PQfname(result,i); for(rowIndex=0;rowIndexnext = ast_variable_new(fieldnames[i], chunk); if (prev->next) { prev = prev->next; } } else { prev = var = ast_variable_new(fieldnames[i], chunk); } } } } } free(fieldnames); } else { ast_log(LOG_WARNING, "Postgresql RealTime: Could not find any rows in table %s.\n", table); } 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; int num_rows = 0; char sql[256]; const char *initfield = NULL; char *stringp; char *chunk; char *op; const char *newparam, *newval; struct ast_realloca ra; struct ast_variable *var=NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; if(!table) { ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n"); return NULL; } memset(&ra, 0, sizeof(ra)); cfg = ast_config_new(); if (!cfg) { /* If I can't alloc memory at this point, why bother doing anything else? */ ast_log(LOG_WARNING, "Out of memory!\n"); 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 *); if(!newparam || !newval) { ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn=NULL; }; return NULL; } initfield = ast_strdupa(newparam); if(initfield && (op = strchr(initfield, ' '))) { *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 */ if(!strchr(newparam, ' ')) op = " ="; else op = ""; snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval); while((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if(!strchr(newparam, ' ')) op = " ="; else op = ""; snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval); } if(initfield) { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); } va_end(ap); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if(!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return NULL; } if(!(result=PQexec(pgsqlConn, sql))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; } 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"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result),PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return NULL; } } ast_log(LOG_DEBUG, "2Postgresql RealTime: Result=%p Query: %s\n", result, sql); if((num_rows=PQntuples(result))>0) { int numFields = PQnfields(result); int i = 0; int rowIndex=0; char** fieldnames=NULL; ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows); fieldnames=malloc(numFields*sizeof(char*)); if (!fieldnames) { /* If I can't alloc memory at this point, why bother doing anything else? */ ast_log(LOG_WARNING, "Out of memory!\n"); ast_mutex_unlock(&pgsql_lock); PQclear(result); return NULL; } for(i = 0; i < numFields; i++) fieldnames[i]=PQfname(result,i); for(rowIndex=0;rowIndex= 0) return (int)numrows; return -1; } static struct ast_config *config_pgsql(const char *database, const char *table, const char *file, struct ast_config *cfg) { PGresult *result = NULL; long num_rows; struct ast_config *new; struct ast_variable *cur_v, *new_v; struct ast_category *cur_cat, *new_cat; char sql[250] = ""; char last[80] = ""; int cat_started = 0; int var_started = 0; int last_cat_metric = 0; last[0] = '\0'; if(!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) { ast_log(LOG_WARNING, "Postgresql RealTime: Cannot configure myself.\n"); return NULL; } snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file); ast_log(LOG_DEBUG, "Postgresql RealTime: Static SQL: %s\n", sql); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); if(!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); return NULL; } if(!(result=PQexec(pgsqlConn, sql))) { ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; } 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"); ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql); ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result),PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); return NULL; } } if((num_rows=PQntuples(result))>0) { int numFields = PQnfields(result); int i = 0; int rowIndex=0; char** fieldnames=NULL; ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows); fieldnames=malloc(numFields*sizeof(char*)); if (!fieldnames) { /* If I can't alloc memory at this point, why bother doing anything else? */ ast_log(LOG_WARNING, "Out of memory!\n"); ast_mutex_unlock(&pgsql_lock); PQclear(result); return NULL; } for(i = 0; i < numFields; i++) fieldnames[i]=PQfname(result,i); for(rowIndex=0;rowIndex 31536000) { ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); } else if (ctime > 86400) { ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60); } else if (ctime > 3600) { ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60); } else if (ctime > 60) { ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60); } else { ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime); } return RESULT_SUCCESS; } else { return RESULT_FAILURE; } }