Wed Apr 6 11:30:09 2011

Asterisk developer's documentation


res_config_pgsql.c File Reference

PostgreSQL plugin for Asterisk RealTime Architecture. More...

#include "asterisk.h"
#include <libpq-fe.h>
#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"

Go to the source code of this file.

Data Structures

struct  columns
struct  psql_tables
struct  tables
struct  tables::psql_columns

Defines

#define ESCAPE_STRING(buffer, stringname)
#define has_schema_support   (version > 70300 ? 1 : 0)
#define MAX_DB_OPTION_SIZE   64
#define release_table(table)   ast_rwlock_unlock(&(table)->lock);
#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Enumerations

enum  { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR }

Functions

static void __init_escapebuf_buf (void)
static void __init_findtable_buf (void)
static void __init_semibuf_buf (void)
static void __init_sql_buf (void)
static void __init_where_buf (void)
static void __reg_module (void)
static void __unreg_module (void)
static struct ast_configconfig_pgsql (const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl, const char *who_asked)
static int destroy_pgsql (const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
static void destroy_table (struct tables *table)
static struct columnsfind_column (struct tables *t, const char *colname)
static struct tablesfind_table (const char *orig_tablename)
static char * handle_cli_realtime_pgsql_cache (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_realtime_pgsql_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int load_module (void)
static int parse_config (int reload)
static int pgsql_reconnect (const char *database)
static struct ast_configrealtime_multi_pgsql (const char *database, const char *table, va_list ap)
static struct ast_variablerealtime_pgsql (const char *database, const char *tablename, va_list ap)
static int reload (void)
static int require_pgsql (const char *database, const char *tablename, va_list ap)
static int store_pgsql (const char *database, const char *table, va_list ap)
static int unload_module (void)
static int unload_pgsql (const char *database, const char *tablename)
static int update2_pgsql (const char *database, const char *tablename, va_list ap)
static int update_pgsql (const char *database, const char *tablename, const char *keyfield, const char *lookup, va_list ap)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL RealTime Configuration Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_realtime []
static time_t connect_time = 0
static char dbhost [MAX_DB_OPTION_SIZE] = ""
static char dbname [MAX_DB_OPTION_SIZE] = ""
static char dbpass [MAX_DB_OPTION_SIZE] = ""
static int dbport = 5432
static char dbsock [MAX_DB_OPTION_SIZE] = ""
static char dbuser [MAX_DB_OPTION_SIZE] = ""
static struct ast_threadstorage escapebuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_escapebuf_buf , .custom_init = NULL , }
static struct ast_threadstorage findtable_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_findtable_buf , .custom_init = NULL , }
static struct ast_config_engine pgsql_engine
static ast_mutex_t pgsql_lock = { { { NULL }, { 0 }, 0, { NULL }, { 0 }, {{{ 0 }}}, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP } , 1, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP }
static PGconn * pgsqlConn = NULL
static enum { ... }  requirements
static struct ast_threadstorage semibuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_semibuf_buf , .custom_init = NULL , }
static struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , }
static int version
static struct ast_threadstorage where_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_where_buf , .custom_init = NULL , }


Detailed Description

PostgreSQL plugin for Asterisk RealTime Architecture.

Author:
Mark Spencer <markster@digium.com>

Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor

ExtRef:
PostgreSQL http://www.postgresql.org

Definition in file res_config_pgsql.c.


Define Documentation

#define ESCAPE_STRING ( buffer,
stringname   ) 

Definition at line 97 of file res_config_pgsql.c.

Referenced by destroy_pgsql(), realtime_multi_pgsql(), realtime_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

#define has_schema_support   (version > 70300 ? 1 : 0)

Definition at line 55 of file res_config_pgsql.c.

Referenced by find_table().

#define MAX_DB_OPTION_SIZE   64

Definition at line 57 of file res_config_pgsql.c.

#define release_table ( table   )     ast_rwlock_unlock(&(table)->lock);

Definition at line 279 of file res_config_pgsql.c.

Referenced by cdr_handler(), handle_cli_realtime_pgsql_cache(), require_pgsql(), update2_pgsql(), and update_pgsql().

#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Definition at line 51 of file res_config_pgsql.c.

Referenced by config_pgsql(), and parse_config().


Enumeration Type Documentation

anonymous enum

Enumerator:
RQ_WARN 
RQ_CREATECLOSE 
RQ_CREATECHAR 

Definition at line 90 of file res_config_pgsql.c.


Function Documentation

static void __init_escapebuf_buf ( void   )  [static]

Definition at line 48 of file res_config_pgsql.c.

00055 : 0)

static void __init_findtable_buf ( void   )  [static]

Definition at line 46 of file res_config_pgsql.c.

00055 : 0)

static void __init_semibuf_buf ( void   )  [static]

Definition at line 49 of file res_config_pgsql.c.

00055 : 0)

static void __init_sql_buf ( void   )  [static]

Definition at line 45 of file res_config_pgsql.c.

00055 : 0)

static void __init_where_buf ( void   )  [static]

Definition at line 47 of file res_config_pgsql.c.

00055 : 0)

static void __reg_module ( void   )  [static]

Definition at line 1614 of file res_config_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 1614 of file res_config_pgsql.c.

static struct ast_config* config_pgsql ( const char *  database,
const char *  table,
const char *  file,
struct ast_config cfg,
struct ast_flags  flags,
const char *  suggested_incl,
const char *  who_asked 
) [static]

Definition at line 1040 of file res_config_pgsql.c.

References ast_category_append(), ast_category_new(), ast_config_internal_load(), ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_variable_append(), ast_variable_new(), last, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RES_CONFIG_PGSQL_CONF, and sql_buf.

01043 {
01044    PGresult *result = NULL;
01045    long num_rows;
01046    struct ast_variable *new_v;
01047    struct ast_category *cur_cat = NULL;
01048    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01049    char last[80] = "";
01050    int last_cat_metric = 0;
01051 
01052    last[0] = '\0';
01053 
01054    if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01055       ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01056       return NULL;
01057    }
01058 
01059    ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01060          "WHERE filename='%s' and commented=0"
01061          "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01062 
01063    ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01064 
01065    /* We now have our complete statement; Lets connect to the server and execute it. */
01066    ast_mutex_lock(&pgsql_lock);
01067    if (!pgsql_reconnect(database)) {
01068       ast_mutex_unlock(&pgsql_lock);
01069       return NULL;
01070    }
01071 
01072    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01073       ast_log(LOG_WARNING,
01074             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", table, database);
01075       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01076       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01077       ast_mutex_unlock(&pgsql_lock);
01078       return NULL;
01079    } else {
01080       ExecStatusType result_status = PQresultStatus(result);
01081       if (result_status != PGRES_COMMAND_OK
01082          && result_status != PGRES_TUPLES_OK
01083          && result_status != PGRES_NONFATAL_ERROR) {
01084          ast_log(LOG_WARNING,
01085                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01086          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01087          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01088                   PQresultErrorMessage(result), PQresStatus(result_status));
01089          ast_mutex_unlock(&pgsql_lock);
01090          return NULL;
01091       }
01092    }
01093 
01094    if ((num_rows = PQntuples(result)) > 0) {
01095       int rowIndex = 0;
01096 
01097       ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01098 
01099       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01100          char *field_category = PQgetvalue(result, rowIndex, 0);
01101          char *field_var_name = PQgetvalue(result, rowIndex, 1);
01102          char *field_var_val = PQgetvalue(result, rowIndex, 2);
01103          char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01104          if (!strcmp(field_var_name, "#include")) {
01105             if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01106                PQclear(result);
01107                ast_mutex_unlock(&pgsql_lock);
01108                return NULL;
01109             }
01110             continue;
01111          }
01112 
01113          if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01114             cur_cat = ast_category_new(field_category, "", 99999);
01115             if (!cur_cat)
01116                break;
01117             strcpy(last, field_category);
01118             last_cat_metric = atoi(field_cat_metric);
01119             ast_category_append(cfg, cur_cat);
01120          }
01121          new_v = ast_variable_new(field_var_name, field_var_val, "");
01122          ast_variable_append(cur_cat, new_v);
01123       }
01124    } else {
01125       ast_log(LOG_WARNING,
01126             "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01127    }
01128 
01129    PQclear(result);
01130    ast_mutex_unlock(&pgsql_lock);
01131 
01132    return cfg;
01133 }

static int destroy_pgsql ( const char *  database,
const char *  table,
const char *  keyfield,
const char *  lookup,
va_list  ap 
) [static]

Definition at line 948 of file res_config_pgsql.c.

References ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strlen_zero(), buf1, buf2, ESCAPE_STRING, escapebuf_buf, LOG_WARNING, pgsql_lock, pgsql_reconnect(), sql_buf, and where_buf.

00949 {
00950    PGresult *result = NULL;
00951    int numrows = 0;
00952    int pgresult;
00953    struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
00954    struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
00955    const char *newparam, *newval;
00956 
00957    if (!table) {
00958       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00959       return -1;
00960    }
00961 
00962    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00963    /*newparam = va_arg(ap, const char *);
00964    newval = va_arg(ap, const char *);
00965    if (!newparam || !newval) {*/
00966    if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup))  {
00967       ast_log(LOG_WARNING,
00968             "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
00969       if (pgsqlConn) {
00970          PQfinish(pgsqlConn);
00971          pgsqlConn = NULL;
00972       };
00973       return -1;
00974    }
00975 
00976    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
00977    ast_mutex_lock(&pgsql_lock);
00978    if (!pgsql_reconnect(database)) {
00979       ast_mutex_unlock(&pgsql_lock);
00980       return -1;
00981    }
00982 
00983 
00984    /* Create the first part of the query using the first parameter/value pairs we just extracted
00985       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00986 
00987    ESCAPE_STRING(buf1, keyfield);
00988    ESCAPE_STRING(buf2, lookup);
00989    ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
00990    while ((newparam = va_arg(ap, const char *))) {
00991       newval = va_arg(ap, const char *);
00992       ESCAPE_STRING(buf1, newparam);
00993       ESCAPE_STRING(buf2, newval);
00994       ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
00995    }
00996    va_end(ap);
00997 
00998    ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
00999 
01000    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01001       ast_log(LOG_WARNING,
01002             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01003       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01004       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01005       ast_mutex_unlock(&pgsql_lock);
01006       return -1;
01007    } else {
01008       ExecStatusType result_status = PQresultStatus(result);
01009       if (result_status != PGRES_COMMAND_OK
01010          && result_status != PGRES_TUPLES_OK
01011          && result_status != PGRES_NONFATAL_ERROR) {
01012          ast_log(LOG_WARNING,
01013                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01014          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01015          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01016                   PQresultErrorMessage(result), PQresStatus(result_status));
01017          ast_mutex_unlock(&pgsql_lock);
01018          return -1;
01019       }
01020    }
01021 
01022    numrows = atoi(PQcmdTuples(result));
01023    ast_mutex_unlock(&pgsql_lock);
01024 
01025    ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01026 
01027    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
01028     * An integer greater than zero indicates the number of rows affected
01029     * Zero indicates that no records were updated
01030     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
01031     */
01032 
01033    if (numrows >= 0)
01034       return (int) numrows;
01035 
01036    return -1;
01037 }

static void destroy_table ( struct tables table  )  [static]

Definition at line 116 of file res_config_pgsql.c.

References ast_free, AST_LIST_REMOVE_HEAD, ast_rwlock_destroy, ast_rwlock_unlock, ast_rwlock_wrlock, columns::list, and table.

Referenced by find_table(), unload_module(), and unload_pgsql().

00117 {
00118    struct columns *column;
00119    ast_rwlock_wrlock(&table->lock);
00120    while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
00121       ast_free(column);
00122    }
00123    ast_rwlock_unlock(&table->lock);
00124    ast_rwlock_destroy(&table->lock);
00125    ast_free(table);
00126 }

static struct columns* find_column ( struct tables t,
const char *  colname 
) [static]

Definition at line 281 of file res_config_pgsql.c.

References AST_LIST_TRAVERSE, tables::columns, columns::list, and columns::name.

Referenced by update2_pgsql(), and update_pgsql().

00282 {
00283    struct columns *column;
00284 
00285    /* Check that the column exists in the table */
00286    AST_LIST_TRAVERSE(&t->columns, column, list) {
00287       if (strcmp(column->name, colname) == 0) {
00288          return column;
00289       }
00290    }
00291    return NULL;
00292 }

static struct tables* find_table ( const char *  orig_tablename  )  [static]

Definition at line 128 of file res_config_pgsql.c.

References ast_calloc, ast_debug, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_rwlock_init, ast_rwlock_rdlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strlen_zero(), ast_verb, destroy_table(), findtable_buf, has_schema_support, columns::list, LOG_ERROR, and table.

Referenced by cdr_handler(), handle_cli_realtime_pgsql_cache(), realtime_require_handler(), require_pgsql(), update2_pgsql(), and update_pgsql().

00129 {
00130    struct columns *column;
00131    struct tables *table;
00132    struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
00133    char *pgerror;
00134    PGresult *result;
00135    char *fname, *ftype, *flen, *fnotnull, *fdef;
00136    int i, rows;
00137 
00138    AST_LIST_LOCK(&psql_tables);
00139    AST_LIST_TRAVERSE(&psql_tables, table, list) {
00140       if (!strcasecmp(table->name, orig_tablename)) {
00141          ast_debug(1, "Found table in cache; now locking\n");
00142          ast_rwlock_rdlock(&table->lock);
00143          ast_debug(1, "Lock cached table; now returning\n");
00144          AST_LIST_UNLOCK(&psql_tables);
00145          return table;
00146       }
00147    }
00148 
00149    ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
00150 
00151    /* Not found, scan the table */
00152    if (has_schema_support) {
00153       char *schemaname, *tablename;
00154       if (strchr(orig_tablename, '.')) {
00155          schemaname = ast_strdupa(orig_tablename);
00156          tablename = strchr(schemaname, '.');
00157          *tablename++ = '\0';
00158       } else {
00159          schemaname = "";
00160          tablename = ast_strdupa(orig_tablename);
00161       }
00162 
00163       /* Escape special characters in schemaname */
00164       if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00165          char *tmp = schemaname, *ptr;
00166 
00167          ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00168          for (; *tmp; tmp++) {
00169             if (strchr("\\'", *tmp)) {
00170                *ptr++ = *tmp;
00171             }
00172             *ptr++ = *tmp;
00173          }
00174          *ptr = '\0';
00175       }
00176       /* Escape special characters in tablename */
00177       if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00178          char *tmp = tablename, *ptr;
00179 
00180          ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00181          for (; *tmp; tmp++) {
00182             if (strchr("\\'", *tmp)) {
00183                *ptr++ = *tmp;
00184             }
00185             *ptr++ = *tmp;
00186          }
00187          *ptr = '\0';
00188       }
00189 
00190       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",
00191          tablename,
00192          ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00193    } else {
00194       /* Escape special characters in tablename */
00195       if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
00196          const char *tmp = orig_tablename;
00197          char *ptr;
00198 
00199          orig_tablename = ptr = alloca(strlen(tmp) * 2 + 1);
00200          for (; *tmp; tmp++) {
00201             if (strchr("\\'", *tmp)) {
00202                *ptr++ = *tmp;
00203             }
00204             *ptr++ = *tmp;
00205          }
00206          *ptr = '\0';
00207       }
00208 
00209       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);
00210    }
00211 
00212    result = PQexec(pgsqlConn, ast_str_buffer(sql));
00213    ast_debug(1, "Query of table structure complete.  Now retrieving results.\n");
00214    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00215       pgerror = PQresultErrorMessage(result);
00216       ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00217       PQclear(result);
00218       AST_LIST_UNLOCK(&psql_tables);
00219       return NULL;
00220    }
00221 
00222    if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
00223       ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
00224       AST_LIST_UNLOCK(&psql_tables);
00225       return NULL;
00226    }
00227    strcpy(table->name, orig_tablename); /* SAFE */
00228    ast_rwlock_init(&table->lock);
00229    AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
00230 
00231    rows = PQntuples(result);
00232    for (i = 0; i < rows; i++) {
00233       fname = PQgetvalue(result, i, 0);
00234       ftype = PQgetvalue(result, i, 1);
00235       flen = PQgetvalue(result, i, 2);
00236       fnotnull = PQgetvalue(result, i, 3);
00237       fdef = PQgetvalue(result, i, 4);
00238       ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00239 
00240       if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
00241          ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
00242          destroy_table(table);
00243          AST_LIST_UNLOCK(&psql_tables);
00244          return NULL;
00245       }
00246 
00247       if (strcmp(flen, "-1") == 0) {
00248          /* Some types, like chars, have the length stored in a different field */
00249          flen = PQgetvalue(result, i, 5);
00250          sscanf(flen, "%30d", &column->len);
00251          column->len -= 4;
00252       } else {
00253          sscanf(flen, "%30d", &column->len);
00254       }
00255       column->name = (char *)column + sizeof(*column);
00256       column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
00257       strcpy(column->name, fname);
00258       strcpy(column->type, ftype);
00259       if (*fnotnull == 't') {
00260          column->notnull = 1;
00261       } else {
00262          column->notnull = 0;
00263       }
00264       if (!ast_strlen_zero(fdef)) {
00265          column->hasdefault = 1;
00266       } else {
00267          column->hasdefault = 0;
00268       }
00269       AST_LIST_INSERT_TAIL(&table->columns, column, list);
00270    }
00271    PQclear(result);
00272 
00273    AST_LIST_INSERT_TAIL(&psql_tables, table, list);
00274    ast_rwlock_rdlock(&table->lock);
00275    AST_LIST_UNLOCK(&psql_tables);
00276    return table;
00277 }

static char * handle_cli_realtime_pgsql_cache ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1501 of file res_config_pgsql.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, CLI_GENERATE, CLI_INIT, tables::columns, ast_cli_entry::command, ast_cli_args::fd, find_table(), columns::len, columns::list, ast_cli_args::n, columns::name, tables::name, columns::notnull, release_table, columns::type, ast_cli_entry::usage, and ast_cli_args::word.

01502 {
01503    struct tables *cur;
01504    int l, which;
01505    char *ret = NULL;
01506 
01507    switch (cmd) {
01508    case CLI_INIT:
01509       e->command = "realtime show pgsql cache";
01510       e->usage =
01511          "Usage: realtime show pgsql cache [<table>]\n"
01512          "       Shows table cache for the PostgreSQL RealTime driver\n";
01513       return NULL;
01514    case CLI_GENERATE:
01515       if (a->argc != 4) {
01516          return NULL;
01517       }
01518       l = strlen(a->word);
01519       which = 0;
01520       AST_LIST_LOCK(&psql_tables);
01521       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01522          if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01523             ret = ast_strdup(cur->name);
01524             break;
01525          }
01526       }
01527       AST_LIST_UNLOCK(&psql_tables);
01528       return ret;
01529    }
01530 
01531    if (a->argc == 4) {
01532       /* List of tables */
01533       AST_LIST_LOCK(&psql_tables);
01534       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01535          ast_cli(a->fd, "%s\n", cur->name);
01536       }
01537       AST_LIST_UNLOCK(&psql_tables);
01538    } else if (a->argc == 5) {
01539       /* List of columns */
01540       if ((cur = find_table(a->argv[4]))) {
01541          struct columns *col;
01542          ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01543          ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01544          AST_LIST_TRAVERSE(&cur->columns, col, list) {
01545             ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01546          }
01547          release_table(cur);
01548       } else {
01549          ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01550       }
01551    }
01552    return 0;
01553 }

static char * handle_cli_realtime_pgsql_status ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1555 of file res_config_pgsql.c.

References ast_cli_args::argc, ast_cli(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, connect_time, dbhost, dbname, dbport, dbsock, dbuser, ast_cli_args::fd, status, and ast_cli_entry::usage.

01556 {
01557    char status[256], credentials[100] = "";
01558    int ctimesec = time(NULL) - connect_time;
01559 
01560    switch (cmd) {
01561    case CLI_INIT:
01562       e->command = "realtime show pgsql status";
01563       e->usage =
01564          "Usage: realtime show pgsql status\n"
01565          "       Shows connection information for the PostgreSQL RealTime driver\n";
01566       return NULL;
01567    case CLI_GENERATE:
01568       return NULL;
01569    }
01570 
01571    if (a->argc != 4)
01572       return CLI_SHOWUSAGE;
01573 
01574    if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01575       if (!ast_strlen_zero(dbhost))
01576          snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01577       else if (!ast_strlen_zero(dbsock))
01578          snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01579       else
01580          snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01581 
01582       if (!ast_strlen_zero(dbuser))
01583          snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01584 
01585       if (ctimesec > 31536000)
01586          ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01587                status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01588                (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01589       else if (ctimesec > 86400)
01590          ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01591                credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01592                ctimesec % 60);
01593       else if (ctimesec > 3600)
01594          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01595                ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01596       else if (ctimesec > 60)
01597          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01598                ctimesec % 60);
01599       else
01600          ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01601 
01602       return CLI_SUCCESS;
01603    } else {
01604       return CLI_FAILURE;
01605    }
01606 }

static int load_module ( void   )  [static]

Definition at line 1298 of file res_config_pgsql.c.

References ARRAY_LEN, ast_cli_register_multiple(), ast_config_engine_register(), AST_MODULE_LOAD_DECLINE, ast_verb, cli_realtime, parse_config(), and pgsql_engine.

01299 {
01300    if(!parse_config(0))
01301       return AST_MODULE_LOAD_DECLINE;
01302 
01303    ast_config_engine_register(&pgsql_engine);
01304    ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01305    ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01306 
01307    return 0;
01308 }

static int parse_config ( int  reload  )  [static]

Definition at line 1344 of file res_config_pgsql.c.

References ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), ast_variable_retrieve(), ast_verb, config, CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEMISSING, CONFIG_STATUS_FILEUNCHANGED, dbhost, dbname, dbpass, dbport, dbsock, dbuser, LOG_WARNING, option_debug, pgsql_lock, pgsql_reconnect(), requirements, RES_CONFIG_PGSQL_CONF, RQ_CREATECHAR, RQ_CREATECLOSE, and RQ_WARN.

01345 {
01346    struct ast_config *config;
01347    const char *s;
01348    struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01349 
01350    config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01351    if (config == CONFIG_STATUS_FILEUNCHANGED) {
01352       return 0;
01353    }
01354 
01355    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01356       ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01357       return 0;
01358    }
01359 
01360    ast_mutex_lock(&pgsql_lock);
01361 
01362    if (pgsqlConn) {
01363       PQfinish(pgsqlConn);
01364       pgsqlConn = NULL;
01365    }
01366 
01367    if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01368       ast_log(LOG_WARNING,
01369             "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01370       strcpy(dbuser, "asterisk");
01371    } else {
01372       ast_copy_string(dbuser, s, sizeof(dbuser));
01373    }
01374 
01375    if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01376       ast_log(LOG_WARNING,
01377             "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01378       strcpy(dbpass, "asterisk");
01379    } else {
01380       ast_copy_string(dbpass, s, sizeof(dbpass));
01381    }
01382 
01383    if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01384       ast_log(LOG_WARNING,
01385             "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01386       dbhost[0] = '\0';
01387    } else {
01388       ast_copy_string(dbhost, s, sizeof(dbhost));
01389    }
01390 
01391    if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01392       ast_log(LOG_WARNING,
01393             "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01394       strcpy(dbname, "asterisk");
01395    } else {
01396       ast_copy_string(dbname, s, sizeof(dbname));
01397    }
01398 
01399    if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01400       ast_log(LOG_WARNING,
01401             "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01402       dbport = 5432;
01403    } else {
01404       dbport = atoi(s);
01405    }
01406 
01407    if (!ast_strlen_zero(dbhost)) {
01408       /* No socket needed */
01409    } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01410       ast_log(LOG_WARNING,
01411             "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
01412       strcpy(dbsock, "/tmp");
01413    } else {
01414       ast_copy_string(dbsock, s, sizeof(dbsock));
01415    }
01416 
01417    if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01418       ast_log(LOG_WARNING,
01419             "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01420       requirements = RQ_WARN;
01421    } else if (!strcasecmp(s, "createclose")) {
01422       requirements = RQ_CREATECLOSE;
01423    } else if (!strcasecmp(s, "createchar")) {
01424       requirements = RQ_CREATECHAR;
01425    }
01426 
01427    ast_config_destroy(config);
01428 
01429    if (option_debug) {
01430       if (!ast_strlen_zero(dbhost)) {
01431          ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01432          ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01433       } else {
01434          ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01435       }
01436       ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01437       ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01438       ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01439    }
01440 
01441    if (!pgsql_reconnect(NULL)) {
01442       ast_log(LOG_WARNING,
01443             "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01444       ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01445    }
01446 
01447    ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01448 
01449    /* Done reloading. Release lock so others can now use driver. */
01450    ast_mutex_unlock(&pgsql_lock);
01451 
01452    return 1;
01453 }

static int pgsql_reconnect ( const char *  database  )  [static]

Definition at line 1455 of file res_config_pgsql.c.

References ast_copy_string(), ast_debug, ast_free, ast_log(), ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_size(), ast_strlen_zero(), connect_time, dbhost, dbname, dbpass, dbport, dbsock, dbuser, LOG_ERROR, and S_OR.

Referenced by config_pgsql(), destroy_pgsql(), parse_config(), realtime_multi_pgsql(), realtime_pgsql(), require_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

01456 {
01457    char my_database[50];
01458 
01459    ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01460 
01461    /* mutex lock should have been locked before calling this function. */
01462 
01463    if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01464       PQfinish(pgsqlConn);
01465       pgsqlConn = NULL;
01466    }
01467 
01468    /* DB password can legitimately be 0-length */
01469    if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01470       struct ast_str *connInfo = ast_str_create(32);
01471 
01472       ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01473          S_OR(dbhost, dbsock), dbport, my_database, dbuser);
01474       if (!ast_strlen_zero(dbpass))
01475          ast_str_append(&connInfo, 0, " password=%s", dbpass);
01476 
01477       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01478       pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01479       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01480       ast_free(connInfo);
01481       connInfo = NULL;
01482 
01483       ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01484       if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01485          ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01486          connect_time = time(NULL);
01487          version = PQserverVersion(pgsqlConn);
01488          return 1;
01489       } else {
01490          ast_log(LOG_ERROR,
01491                "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01492                dbname, dbhost, PQresultErrorMessage(NULL));
01493          return 0;
01494       }
01495    } else {
01496       ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01497       return 1;
01498    }
01499 }

static struct ast_config* realtime_multi_pgsql ( const char *  database,
const char *  table,
va_list  ap 
) [static]

Definition at line 429 of file res_config_pgsql.c.

References ast_calloc, ast_category_append(), ast_category_new(), ast_category_rename(), ast_config_new(), ast_debug, ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_realtime_decode_chunk(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strip(), ast_strlen_zero(), ast_variable_append(), ast_variable_new(), ESCAPE_STRING, escapebuf_buf, LOG_ERROR, LOG_WARNING, pgsql_lock, pgsql_reconnect(), sql_buf, strsep(), and var.

00430 {
00431    PGresult *result = NULL;
00432    int num_rows = 0, pgresult;
00433    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00434    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00435    const char *initfield = NULL;
00436    char *stringp;
00437    char *chunk;
00438    char *op;
00439    const char *newparam, *newval;
00440    struct ast_variable *var = NULL;
00441    struct ast_config *cfg = NULL;
00442    struct ast_category *cat = NULL;
00443 
00444    if (!table) {
00445       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00446       return NULL;
00447    }
00448 
00449    if (!(cfg = ast_config_new()))
00450       return NULL;
00451 
00452    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00453    newparam = va_arg(ap, const char *);
00454    newval = va_arg(ap, const char *);
00455    if (!newparam || !newval) {
00456       ast_log(LOG_WARNING,
00457             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00458       if (pgsqlConn) {
00459          PQfinish(pgsqlConn);
00460          pgsqlConn = NULL;
00461       }
00462       return NULL;
00463    }
00464 
00465    initfield = ast_strdupa(newparam);
00466    if ((op = strchr(initfield, ' '))) {
00467       *op = '\0';
00468    }
00469 
00470    /* Create the first part of the query using the first parameter/value pairs we just extracted
00471       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00472 
00473    if (!strchr(newparam, ' '))
00474       op = " =";
00475    else
00476       op = "";
00477 
00478    ESCAPE_STRING(escapebuf, newval);
00479    if (pgresult) {
00480       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00481       va_end(ap);
00482       return NULL;
00483    }
00484 
00485    ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
00486    while ((newparam = va_arg(ap, const char *))) {
00487       newval = va_arg(ap, const char *);
00488       if (!strchr(newparam, ' '))
00489          op = " =";
00490       else
00491          op = "";
00492 
00493       ESCAPE_STRING(escapebuf, newval);
00494       if (pgresult) {
00495          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00496          va_end(ap);
00497          return NULL;
00498       }
00499 
00500       ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00501    }
00502 
00503    if (initfield) {
00504       ast_str_append(&sql, 0, " ORDER BY %s", initfield);
00505    }
00506 
00507    va_end(ap);
00508 
00509    /* We now have our complete statement; Lets connect to the server and execute it. */
00510    ast_mutex_lock(&pgsql_lock);
00511    if (!pgsql_reconnect(database)) {
00512       ast_mutex_unlock(&pgsql_lock);
00513       return NULL;
00514    }
00515 
00516    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00517       ast_log(LOG_WARNING,
00518             "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00519       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00520       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00521       ast_mutex_unlock(&pgsql_lock);
00522       return NULL;
00523    } else {
00524       ExecStatusType result_status = PQresultStatus(result);
00525       if (result_status != PGRES_COMMAND_OK
00526          && result_status != PGRES_TUPLES_OK
00527          && result_status != PGRES_NONFATAL_ERROR) {
00528          ast_log(LOG_WARNING,
00529                "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00530          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00531          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00532                   PQresultErrorMessage(result), PQresStatus(result_status));
00533          ast_mutex_unlock(&pgsql_lock);
00534          return NULL;
00535       }
00536    }
00537 
00538    ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00539 
00540    if ((num_rows = PQntuples(result)) > 0) {
00541       int numFields = PQnfields(result);
00542       int i = 0;
00543       int rowIndex = 0;
00544       char **fieldnames = NULL;
00545 
00546       ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00547 
00548       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00549          ast_mutex_unlock(&pgsql_lock);
00550          PQclear(result);
00551          return NULL;
00552       }
00553       for (i = 0; i < numFields; i++)
00554          fieldnames[i] = PQfname(result, i);
00555 
00556       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00557          var = NULL;
00558          if (!(cat = ast_category_new("","",99999)))
00559             continue;
00560          for (i = 0; i < numFields; i++) {
00561             stringp = PQgetvalue(result, rowIndex, i);
00562             while (stringp) {
00563                chunk = strsep(&stringp, ";");
00564                if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00565                   if (initfield && !strcmp(initfield, fieldnames[i])) {
00566                      ast_category_rename(cat, chunk);
00567                   }
00568                   var = ast_variable_new(fieldnames[i], chunk, "");
00569                   ast_variable_append(cat, var);
00570                }
00571             }
00572          }
00573          ast_category_append(cfg, cat);
00574       }
00575       ast_free(fieldnames);
00576    } else {
00577       ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
00578    }
00579 
00580    ast_mutex_unlock(&pgsql_lock);
00581    PQclear(result);
00582 
00583    return cfg;
00584 }

static struct ast_variable* realtime_pgsql ( const char *  database,
const char *  tablename,
va_list  ap 
) [static]

Definition at line 294 of file res_config_pgsql.c.

References ast_calloc, ast_debug, ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_realtime_decode_chunk(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strip(), ast_strlen_zero(), ast_variable_new(), ESCAPE_STRING, escapebuf_buf, LOG_ERROR, LOG_WARNING, ast_variable::next, pgsql_lock, pgsql_reconnect(), sql_buf, strsep(), and var.

00295 {
00296    PGresult *result = NULL;
00297    int num_rows = 0, pgresult;
00298    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00299    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00300    char *stringp;
00301    char *chunk;
00302    char *op;
00303    const char *newparam, *newval;
00304    struct ast_variable *var = NULL, *prev = NULL;
00305 
00306    if (!tablename) {
00307       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00308       return NULL;
00309    }
00310 
00311    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00312    newparam = va_arg(ap, const char *);
00313    newval = va_arg(ap, const char *);
00314    if (!newparam || !newval) {
00315       ast_log(LOG_WARNING,
00316             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00317       if (pgsqlConn) {
00318          PQfinish(pgsqlConn);
00319          pgsqlConn = NULL;
00320       }
00321       return NULL;
00322    }
00323 
00324    /* Create the first part of the query using the first parameter/value pairs we just extracted
00325       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00326    op = strchr(newparam, ' ') ? "" : " =";
00327 
00328    ESCAPE_STRING(escapebuf, newval);
00329    if (pgresult) {
00330       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00331       va_end(ap);
00332       return NULL;
00333    }
00334 
00335    ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
00336    while ((newparam = va_arg(ap, const char *))) {
00337       newval = va_arg(ap, const char *);
00338       if (!strchr(newparam, ' '))
00339          op = " =";
00340       else
00341          op = "";
00342 
00343       ESCAPE_STRING(escapebuf, newval);
00344       if (pgresult) {
00345          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00346          va_end(ap);
00347          return NULL;
00348       }
00349 
00350       ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00351    }
00352    va_end(ap);
00353 
00354    /* We now have our complete statement; Lets connect to the server and execute it. */
00355    ast_mutex_lock(&pgsql_lock);
00356    if (!pgsql_reconnect(database)) {
00357       ast_mutex_unlock(&pgsql_lock);
00358       return NULL;
00359    }
00360 
00361    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00362       ast_log(LOG_WARNING,
00363             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
00364       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00365       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00366       ast_mutex_unlock(&pgsql_lock);
00367       return NULL;
00368    } else {
00369       ExecStatusType result_status = PQresultStatus(result);
00370       if (result_status != PGRES_COMMAND_OK
00371          && result_status != PGRES_TUPLES_OK
00372          && result_status != PGRES_NONFATAL_ERROR) {
00373          ast_log(LOG_WARNING,
00374                "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
00375          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00376          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00377                   PQresultErrorMessage(result), PQresStatus(result_status));
00378          ast_mutex_unlock(&pgsql_lock);
00379          return NULL;
00380       }
00381    }
00382 
00383    ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00384 
00385    if ((num_rows = PQntuples(result)) > 0) {
00386       int i = 0;
00387       int rowIndex = 0;
00388       int numFields = PQnfields(result);
00389       char **fieldnames = NULL;
00390 
00391       ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00392 
00393       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00394          ast_mutex_unlock(&pgsql_lock);
00395          PQclear(result);
00396          return NULL;
00397       }
00398       for (i = 0; i < numFields; i++)
00399          fieldnames[i] = PQfname(result, i);
00400       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00401          for (i = 0; i < numFields; i++) {
00402             stringp = PQgetvalue(result, rowIndex, i);
00403             while (stringp) {
00404                chunk = strsep(&stringp, ";");
00405                if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00406                   if (prev) {
00407                      prev->next = ast_variable_new(fieldnames[i], chunk, "");
00408                      if (prev->next) {
00409                         prev = prev->next;
00410                      }
00411                   } else {
00412                      prev = var = ast_variable_new(fieldnames[i], chunk, "");
00413                   }
00414                }
00415             }
00416          }
00417       }
00418       ast_free(fieldnames);
00419    } else {
00420       ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
00421    }
00422 
00423    ast_mutex_unlock(&pgsql_lock);
00424    PQclear(result);
00425 
00426    return var;
00427 }

static int reload ( void   )  [static]

Definition at line 1337 of file res_config_pgsql.c.

References parse_config().

01338 {
01339    parse_config(1);
01340 
01341    return 0;
01342 }

static int require_pgsql ( const char *  database,
const char *  tablename,
va_list  ap 
) [static]

Definition at line 1135 of file res_config_pgsql.c.

References ast_debug, ast_free, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_rq_is_int(), ast_str_buffer(), ast_str_create(), ast_str_set(), find_table(), columns::len, columns::list, LOG_ERROR, LOG_WARNING, columns::name, pgsql_lock, pgsql_reconnect(), release_table, requirements, RQ_CHAR, RQ_CREATECHAR, RQ_DATE, RQ_DATETIME, RQ_FLOAT, RQ_INTEGER1, RQ_INTEGER2, RQ_INTEGER3, RQ_INTEGER4, RQ_INTEGER8, RQ_UINTEGER1, RQ_UINTEGER2, RQ_UINTEGER3, RQ_UINTEGER4, RQ_UINTEGER8, RQ_WARN, columns::size, table, and columns::type.

01136 {
01137    struct columns *column;
01138    struct tables *table = find_table(tablename);
01139    char *elm;
01140    int type, size, res = 0;
01141 
01142    if (!table) {
01143       ast_log(LOG_WARNING, "Table %s not found in database.  This table should exist if you're using realtime.\n", tablename);
01144       return -1;
01145    }
01146 
01147    while ((elm = va_arg(ap, char *))) {
01148       type = va_arg(ap, require_type);
01149       size = va_arg(ap, int);
01150       AST_LIST_TRAVERSE(&table->columns, column, list) {
01151          if (strcmp(column->name, elm) == 0) {
01152             /* Char can hold anything, as long as it is large enough */
01153             if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01154                if ((size > column->len) && column->len != -1) {
01155                   ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01156                   res = -1;
01157                }
01158             } else if (strncmp(column->type, "int", 3) == 0) {
01159                int typesize = atoi(column->type + 3);
01160                /* Integers can hold only other integers */
01161                if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01162                   type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01163                   type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01164                   type == RQ_UINTEGER2) && typesize == 2) {
01165                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01166                   res = -1;
01167                } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01168                   type == RQ_UINTEGER4) && typesize == 4) {
01169                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01170                   res = -1;
01171                } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01172                   ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01173                      column->name,
01174                         type == RQ_CHAR ? "char" :
01175                         type == RQ_DATETIME ? "datetime" :
01176                         type == RQ_DATE ? "date" :
01177                         type == RQ_FLOAT ? "float" :
01178                         "a rather stiff drink ",
01179                      size, column->type);
01180                   res = -1;
01181                }
01182             } else if (strncmp(column->type, "float", 5) == 0) {
01183                if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01184                   ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01185                   res = -1;
01186                }
01187             } else if (strncmp(column->type, "timestamp", 9) == 0) {
01188                if (type != RQ_DATETIME && type != RQ_DATE) {
01189                   ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01190                   res = -1;
01191                }
01192             } else { /* There are other types that no module implements yet */
01193                ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01194                res = -1;
01195             }
01196             break;
01197          }
01198       }
01199 
01200       if (!column) {
01201          if (requirements == RQ_WARN) {
01202             ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01203          } else {
01204             struct ast_str *sql = ast_str_create(100);
01205             char fieldtype[15];
01206             PGresult *result;
01207 
01208             if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01209                /* Size is minimum length; make it at least 50% greater,
01210                 * just to be sure, because PostgreSQL doesn't support
01211                 * resizing columns. */
01212                snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01213                   size < 15 ? size * 2 :
01214                   (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01215             } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01216                snprintf(fieldtype, sizeof(fieldtype), "INT2");
01217             } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01218                snprintf(fieldtype, sizeof(fieldtype), "INT4");
01219             } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01220                snprintf(fieldtype, sizeof(fieldtype), "INT8");
01221             } else if (type == RQ_UINTEGER8) {
01222                /* No such type on PostgreSQL */
01223                snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01224             } else if (type == RQ_FLOAT) {
01225                snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01226             } else if (type == RQ_DATE) {
01227                snprintf(fieldtype, sizeof(fieldtype), "DATE");
01228             } else if (type == RQ_DATETIME) {
01229                snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01230             } else {
01231                ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01232                ast_free(sql);
01233                continue;
01234             }
01235             ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01236             ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01237 
01238             ast_mutex_lock(&pgsql_lock);
01239             if (!pgsql_reconnect(database)) {
01240                ast_mutex_unlock(&pgsql_lock);
01241                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01242                ast_free(sql);
01243                continue;
01244             }
01245 
01246             ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01247             result = PQexec(pgsqlConn, ast_str_buffer(sql));
01248             ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01249             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01250                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01251             }
01252             PQclear(result);
01253             ast_mutex_unlock(&pgsql_lock);
01254 
01255             ast_free(sql);
01256          }
01257       }
01258    }
01259    release_table(table);
01260    return res;
01261 }

static int store_pgsql ( const char *  database,
const char *  table,
va_list  ap 
) [static]

Definition at line 856 of file res_config_pgsql.c.

References ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ESCAPE_STRING, escapebuf_buf, LOG_WARNING, pgsql_lock, pgsql_reconnect(), sql_buf, and where_buf.

00857 {
00858    PGresult *result = NULL;
00859    Oid insertid;
00860    struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
00861    struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
00862    struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
00863    int pgresult;
00864    const char *newparam, *newval;
00865 
00866    if (!table) {
00867       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00868       return -1;
00869    }
00870 
00871    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00872    newparam = va_arg(ap, const char *);
00873    newval = va_arg(ap, const char *);
00874    if (!newparam || !newval) {
00875       ast_log(LOG_WARNING,
00876             "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
00877       if (pgsqlConn) {
00878          PQfinish(pgsqlConn);
00879          pgsqlConn = NULL;
00880       }
00881       return -1;
00882    }
00883 
00884    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
00885    ast_mutex_lock(&pgsql_lock);
00886    if (!pgsql_reconnect(database)) {
00887       ast_mutex_unlock(&pgsql_lock);
00888       return -1;
00889    }
00890 
00891    /* Create the first part of the query using the first parameter/value pairs we just extracted
00892       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00893    ESCAPE_STRING(buf, newparam);
00894    ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
00895    ESCAPE_STRING(buf, newval);
00896    ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
00897    while ((newparam = va_arg(ap, const char *))) {
00898       newval = va_arg(ap, const char *);
00899       ESCAPE_STRING(buf, newparam);
00900       ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
00901       ESCAPE_STRING(buf, newval);
00902       ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
00903    }
00904    va_end(ap);
00905    ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00906 
00907    ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
00908 
00909    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql1)))) {
00910       ast_log(LOG_WARNING,
00911             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00912       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00913       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00914       ast_mutex_unlock(&pgsql_lock);
00915       return -1;
00916    } else {
00917       ExecStatusType result_status = PQresultStatus(result);
00918       if (result_status != PGRES_COMMAND_OK
00919          && result_status != PGRES_TUPLES_OK
00920          && result_status != PGRES_NONFATAL_ERROR) {
00921          ast_log(LOG_WARNING,
00922                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00923          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00924          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00925                   PQresultErrorMessage(result), PQresStatus(result_status));
00926          ast_mutex_unlock(&pgsql_lock);
00927          return -1;
00928       }
00929    }
00930 
00931    insertid = PQoidValue(result);
00932    ast_mutex_unlock(&pgsql_lock);
00933 
00934    ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
00935 
00936    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00937     * An integer greater than zero indicates the number of rows affected
00938     * Zero indicates that no records were updated
00939     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00940     */
00941 
00942    if (insertid >= 0)
00943       return (int) insertid;
00944 
00945    return -1;
00946 }

static int unload_module ( void   )  [static]

Definition at line 1310 of file res_config_pgsql.c.

References ARRAY_LEN, ast_cli_unregister_multiple(), ast_config_engine_deregister(), AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_verb, cli_realtime, destroy_table(), columns::list, pgsql_engine, pgsql_lock, and table.

01311 {
01312    struct tables *table;
01313    /* Acquire control before doing anything to the module itself. */
01314    ast_mutex_lock(&pgsql_lock);
01315 
01316    if (pgsqlConn) {
01317       PQfinish(pgsqlConn);
01318       pgsqlConn = NULL;
01319    }
01320    ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01321    ast_config_engine_deregister(&pgsql_engine);
01322    ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01323 
01324    /* Destroy cached table info */
01325    AST_LIST_LOCK(&psql_tables);
01326    while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01327       destroy_table(table);
01328    }
01329    AST_LIST_UNLOCK(&psql_tables);
01330 
01331    /* Unlock so something else can destroy the lock. */
01332    ast_mutex_unlock(&pgsql_lock);
01333 
01334    return 0;
01335 }

static int unload_pgsql ( const char *  database,
const char *  tablename 
) [static]

Definition at line 1263 of file res_config_pgsql.c.

References ast_debug, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, destroy_table(), columns::list, and tables::name.

01264 {
01265    struct tables *cur;
01266    ast_debug(2, "About to lock table cache list\n");
01267    AST_LIST_LOCK(&psql_tables);
01268    ast_debug(2, "About to traverse table cache list\n");
01269    AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01270       if (strcmp(cur->name, tablename) == 0) {
01271          ast_debug(2, "About to remove matching cache entry\n");
01272          AST_LIST_REMOVE_CURRENT(list);
01273          ast_debug(2, "About to destroy matching cache entry\n");
01274          destroy_table(cur);
01275          ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01276          break;
01277       }
01278    }
01279    AST_LIST_TRAVERSE_SAFE_END
01280    AST_LIST_UNLOCK(&psql_tables);
01281    ast_debug(2, "About to return\n");
01282    return cur ? 0 : -1;
01283 }

static int update2_pgsql ( const char *  database,
const char *  tablename,
va_list  ap 
) [static]

Definition at line 724 of file res_config_pgsql.c.

References ast_debug, ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), first, LOG_ERROR, LOG_NOTICE, LOG_WARNING, pgsql_lock, pgsql_reconnect(), release_table, sql_buf, table, and where_buf.

00725 {
00726    PGresult *result = NULL;
00727    int numrows = 0, pgresult, first = 1;
00728    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
00729    const char *newparam, *newval;
00730    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00731    struct ast_str *where = ast_str_thread_get(&where_buf, 100);
00732    struct tables *table;
00733 
00734    if (!tablename) {
00735       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00736       return -1;
00737    }
00738 
00739    if (!escapebuf || !sql || !where) {
00740       /* Memory error, already handled */
00741       return -1;
00742    }
00743 
00744    if (!(table = find_table(tablename))) {
00745       ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00746       return -1;
00747    }
00748 
00749    ast_str_set(&sql, 0, "UPDATE %s SET ", tablename);
00750    ast_str_set(&where, 0, "WHERE");
00751 
00752    while ((newparam = va_arg(ap, const char *))) {
00753       if (!find_column(table, newparam)) {
00754          ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
00755          release_table(table);
00756          return -1;
00757       }
00758 
00759       newval = va_arg(ap, const char *);
00760       ESCAPE_STRING(escapebuf, newval);
00761       if (pgresult) {
00762          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00763          release_table(table);
00764          ast_free(sql);
00765          return -1;
00766       }
00767       ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
00768       first = 0;
00769    }
00770 
00771    if (first) {
00772       ast_log(LOG_WARNING,
00773             "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
00774       if (pgsqlConn) {
00775          PQfinish(pgsqlConn);
00776          pgsqlConn = NULL;
00777       }
00778       release_table(table);
00779       return -1;
00780    }
00781 
00782    /* Now retrieve the columns to update */
00783    first = 1;
00784    while ((newparam = va_arg(ap, const char *))) {
00785       newval = va_arg(ap, const char *);
00786 
00787       /* If the column is not within the table, then skip it */
00788       if (!find_column(table, newparam)) {
00789          ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
00790          continue;
00791       }
00792 
00793       ESCAPE_STRING(escapebuf, newval);
00794       if (pgresult) {
00795          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00796          release_table(table);
00797          ast_free(sql);
00798          return -1;
00799       }
00800 
00801       ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
00802    }
00803    release_table(table);
00804 
00805    ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
00806 
00807    ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00808 
00809    /* We now have our complete statement; connect to the server and execute it. */
00810    ast_mutex_lock(&pgsql_lock);
00811    if (!pgsql_reconnect(database)) {
00812       ast_mutex_unlock(&pgsql_lock);
00813       return -1;
00814    }
00815 
00816    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00817       ast_log(LOG_WARNING,
00818             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00819       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00820       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00821       ast_mutex_unlock(&pgsql_lock);
00822       return -1;
00823    } else {
00824       ExecStatusType result_status = PQresultStatus(result);
00825       if (result_status != PGRES_COMMAND_OK
00826          && result_status != PGRES_TUPLES_OK
00827          && result_status != PGRES_NONFATAL_ERROR) {
00828          ast_log(LOG_WARNING,
00829                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00830          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00831          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00832                   PQresultErrorMessage(result), PQresStatus(result_status));
00833          ast_mutex_unlock(&pgsql_lock);
00834          return -1;
00835       }
00836    }
00837 
00838    numrows = atoi(PQcmdTuples(result));
00839    ast_mutex_unlock(&pgsql_lock);
00840 
00841    ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00842 
00843    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00844     * An integer greater than zero indicates the number of rows affected
00845     * Zero indicates that no records were updated
00846     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00847     */
00848 
00849    if (numrows >= 0) {
00850       return (int) numrows;
00851    }
00852 
00853    return -1;
00854 }

static int update_pgsql ( const char *  database,
const char *  tablename,
const char *  keyfield,
const char *  lookup,
va_list  ap 
) [static]

Definition at line 586 of file res_config_pgsql.c.

References ast_debug, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, pgsql_lock, pgsql_reconnect(), release_table, sql_buf, and table.

00588 {
00589    PGresult *result = NULL;
00590    int numrows = 0, pgresult;
00591    const char *newparam, *newval;
00592    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00593    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00594    struct tables *table;
00595    struct columns *column = NULL;
00596 
00597    if (!tablename) {
00598       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00599       return -1;
00600    }
00601 
00602    if (!(table = find_table(tablename))) {
00603       ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00604       return -1;
00605    }
00606 
00607    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00608    newparam = va_arg(ap, const char *);
00609    newval = va_arg(ap, const char *);
00610    if (!newparam || !newval) {
00611       ast_log(LOG_WARNING,
00612             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00613       if (pgsqlConn) {
00614          PQfinish(pgsqlConn);
00615          pgsqlConn = NULL;
00616       }
00617       release_table(table);
00618       return -1;
00619    }
00620 
00621    /* Check that the column exists in the table */
00622    AST_LIST_TRAVERSE(&table->columns, column, list) {
00623       if (strcmp(column->name, newparam) == 0) {
00624          break;
00625       }
00626    }
00627 
00628    if (!column) {
00629       ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
00630       release_table(table);
00631       return -1;
00632    }
00633 
00634    /* Create the first part of the query using the first parameter/value pairs we just extracted
00635       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00636 
00637    ESCAPE_STRING(escapebuf, newval);
00638    if (pgresult) {
00639       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00640       va_end(ap);
00641       release_table(table);
00642       return -1;
00643    }
00644    ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
00645 
00646    while ((newparam = va_arg(ap, const char *))) {
00647       newval = va_arg(ap, const char *);
00648 
00649       if (!find_column(table, newparam)) {
00650          ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
00651          continue;
00652       }
00653 
00654       ESCAPE_STRING(escapebuf, newval);
00655       if (pgresult) {
00656          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00657          va_end(ap);
00658          release_table(table);
00659          return -1;
00660       }
00661 
00662       ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
00663    }
00664    va_end(ap);
00665    release_table(table);
00666 
00667    ESCAPE_STRING(escapebuf, lookup);
00668    if (pgresult) {
00669       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
00670       va_end(ap);
00671       return -1;
00672    }
00673 
00674    ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
00675 
00676    ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00677 
00678    /* We now have our complete statement; Lets connect to the server and execute it. */
00679    ast_mutex_lock(&pgsql_lock);
00680    if (!pgsql_reconnect(database)) {
00681       ast_mutex_unlock(&pgsql_lock);
00682       return -1;
00683    }
00684 
00685    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00686       ast_log(LOG_WARNING,
00687             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00688       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00689       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00690       ast_mutex_unlock(&pgsql_lock);
00691       return -1;
00692    } else {
00693       ExecStatusType result_status = PQresultStatus(result);
00694       if (result_status != PGRES_COMMAND_OK
00695          && result_status != PGRES_TUPLES_OK
00696          && result_status != PGRES_NONFATAL_ERROR) {
00697          ast_log(LOG_WARNING,
00698                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00699          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00700          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00701                   PQresultErrorMessage(result), PQresStatus(result_status));
00702          ast_mutex_unlock(&pgsql_lock);
00703          return -1;
00704       }
00705    }
00706 
00707    numrows = atoi(PQcmdTuples(result));
00708    ast_mutex_unlock(&pgsql_lock);
00709 
00710    ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00711 
00712    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00713     * An integer greater than zero indicates the number of rows affected
00714     * Zero indicates that no records were updated
00715     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00716     */
00717 
00718    if (numrows >= 0)
00719       return (int) numrows;
00720 
00721    return -1;
00722 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL RealTime Configuration Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, } [static]

Definition at line 1614 of file res_config_pgsql.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1614 of file res_config_pgsql.c.

struct ast_cli_entry cli_realtime[] [static]

Initial value:

 {
   { .handler =  handle_cli_realtime_pgsql_status , .summary =  "Shows connection information for the PostgreSQL RealTime driver" ,__VA_ARGS__ },
   { .handler =  handle_cli_realtime_pgsql_cache , .summary =  "Shows cached tables within the PostgreSQL realtime driver" ,__VA_ARGS__ },
}

Definition at line 92 of file res_config_pgsql.c.

Referenced by load_module(), and unload_module().

time_t connect_time = 0 [static]

Definition at line 83 of file res_config_pgsql.c.

char dbhost[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 77 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbname[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 80 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbpass[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 79 of file res_config_pgsql.c.

Referenced by parse_config(), and pgsql_reconnect().

int dbport = 5432 [static]

Definition at line 82 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbsock[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 81 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

char dbuser[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 78 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), parse_config(), and pgsql_reconnect().

struct ast_threadstorage escapebuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_escapebuf_buf , .custom_init = NULL , } [static]

Definition at line 48 of file res_config_pgsql.c.

Referenced by destroy_pgsql(), realtime_multi_pgsql(), realtime_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

struct ast_threadstorage findtable_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_findtable_buf , .custom_init = NULL , } [static]

Definition at line 46 of file res_config_pgsql.c.

Referenced by find_table().

struct ast_config_engine pgsql_engine [static]

Definition at line 1285 of file res_config_pgsql.c.

Referenced by load_module(), and unload_module().

ast_mutex_t pgsql_lock = { { { NULL }, { 0 }, 0, { NULL }, { 0 }, {{{ 0 }}}, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP } , 1, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP } [static]

Definition at line 44 of file res_config_pgsql.c.

PGconn* pgsqlConn = NULL [static]

Definition at line 53 of file res_config_pgsql.c.

enum { ... } requirements [static]

Referenced by parse_config(), and require_pgsql().

struct ast_threadstorage semibuf_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_semibuf_buf , .custom_init = NULL , } [static]

Definition at line 49 of file res_config_pgsql.c.

struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , } [static]

Definition at line 45 of file res_config_pgsql.c.

int version [static]

Definition at line 54 of file res_config_pgsql.c.

struct ast_threadstorage where_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_where_buf , .custom_init = NULL , } [static]

Definition at line 47 of file res_config_pgsql.c.

Referenced by destroy_pgsql(), realtime_update2_handler(), store_pgsql(), and update2_pgsql().


Generated on Wed Apr 6 11:30:09 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7