Thu Sep 7 01:03:36 2017

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_columns
struct  psql_tables
struct  tables

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 *database, 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 = "ac1f6a56484a8820659555499174e588" , .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 = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
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   ) 
#define has_schema_support   (version > 70300 ? 1 : 0)

Definition at line 56 of file res_config_pgsql.c.

Referenced by find_table().

#define MAX_DB_OPTION_SIZE   64

Definition at line 58 of file res_config_pgsql.c.

#define release_table ( table   )     ast_rwlock_unlock(&(table)->lock);
#define RES_CONFIG_PGSQL_CONF   "res_pgsql.conf"

Definition at line 52 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 91 of file res_config_pgsql.c.


Function Documentation

static void __init_escapebuf_buf ( void   )  [static]

Definition at line 49 of file res_config_pgsql.c.

00056 : 0)

static void __init_findtable_buf ( void   )  [static]

Definition at line 47 of file res_config_pgsql.c.

00056 : 0)

static void __init_semibuf_buf ( void   )  [static]

Definition at line 50 of file res_config_pgsql.c.

00056 : 0)

static void __init_sql_buf ( void   )  [static]

Definition at line 46 of file res_config_pgsql.c.

00056 : 0)

static void __init_where_buf ( void   )  [static]

Definition at line 48 of file res_config_pgsql.c.

00056 : 0)

static void __reg_module ( void   )  [static]

Definition at line 1669 of file res_config_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 1669 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, read]

Definition at line 1077 of file res_config_pgsql.c.

References ast_category_append(), ast_category_new(), ast_config_internal_load(), ast_copy_string(), 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(), dbname, last, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RAII_VAR, RES_CONFIG_PGSQL_CONF, and sql_buf.

01080 {
01081    RAII_VAR(PGresult *, result, NULL, PQclear);
01082    long num_rows;
01083    struct ast_variable *new_v;
01084    struct ast_category *cur_cat = NULL;
01085    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01086    char last[80];
01087    int last_cat_metric = 0;
01088 
01089    last[0] = '\0';
01090 
01091    /*
01092     * Ignore database from the extconfig.conf since it is
01093     * configured by res_pgsql.conf.
01094     */
01095    database = dbname;
01096 
01097    if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01098       ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01099       return NULL;
01100    }
01101 
01102    ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01103          "WHERE filename='%s' and commented=0"
01104          "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01105 
01106    ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01107 
01108    /* We now have our complete statement; Lets connect to the server and execute it. */
01109    ast_mutex_lock(&pgsql_lock);
01110    if (!pgsql_reconnect(database)) {
01111       ast_mutex_unlock(&pgsql_lock);
01112       return NULL;
01113    }
01114 
01115    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01116       ast_log(LOG_WARNING,
01117             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", table, database);
01118       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01119       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01120       ast_mutex_unlock(&pgsql_lock);
01121       return NULL;
01122    } else {
01123       ExecStatusType result_status = PQresultStatus(result);
01124       if (result_status != PGRES_COMMAND_OK
01125          && result_status != PGRES_TUPLES_OK
01126          && result_status != PGRES_NONFATAL_ERROR) {
01127          ast_log(LOG_WARNING,
01128                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01129          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01130          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01131                   PQresultErrorMessage(result), PQresStatus(result_status));
01132          ast_mutex_unlock(&pgsql_lock);
01133          return NULL;
01134       }
01135    }
01136 
01137    if ((num_rows = PQntuples(result)) > 0) {
01138       int rowIndex = 0;
01139 
01140       ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01141 
01142       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01143          char *field_category = PQgetvalue(result, rowIndex, 0);
01144          char *field_var_name = PQgetvalue(result, rowIndex, 1);
01145          char *field_var_val = PQgetvalue(result, rowIndex, 2);
01146          char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01147          if (!strcmp(field_var_name, "#include")) {
01148             if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01149                ast_mutex_unlock(&pgsql_lock);
01150                return NULL;
01151             }
01152             continue;
01153          }
01154 
01155          if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01156             cur_cat = ast_category_new(field_category, "", 99999);
01157             if (!cur_cat)
01158                break;
01159             ast_copy_string(last, field_category, sizeof(last));
01160             last_cat_metric = atoi(field_cat_metric);
01161             ast_category_append(cfg, cur_cat);
01162          }
01163          new_v = ast_variable_new(field_var_name, field_var_val, "");
01164          ast_variable_append(cur_cat, new_v);
01165       }
01166    } else {
01167       ast_log(LOG_WARNING,
01168             "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01169    }
01170 
01171    ast_mutex_unlock(&pgsql_lock);
01172 
01173    return cfg;
01174 }

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

Definition at line 980 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(), dbname, ESCAPE_STRING, escapebuf_buf, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RAII_VAR, sql_buf, and where_buf.

00981 {
00982    RAII_VAR(PGresult *, result, NULL, PQclear);
00983    int numrows = 0;
00984    int pgresult;
00985    struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
00986    struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
00987    const char *newparam, *newval;
00988 
00989    /*
00990     * Ignore database from the extconfig.conf since it was
00991     * configured by res_pgsql.conf.
00992     */
00993    database = dbname;
00994 
00995    if (!table) {
00996       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00997       return -1;
00998    }
00999 
01000    /* Get the first parameter and first value in our list of passed paramater/value pairs */
01001    /*newparam = va_arg(ap, const char *);
01002    newval = va_arg(ap, const char *);
01003    if (!newparam || !newval) {*/
01004    if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup))  {
01005       ast_log(LOG_WARNING,
01006             "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
01007       if (pgsqlConn) {
01008          PQfinish(pgsqlConn);
01009          pgsqlConn = NULL;
01010       };
01011       return -1;
01012    }
01013 
01014    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
01015    ast_mutex_lock(&pgsql_lock);
01016    if (!pgsql_reconnect(database)) {
01017       ast_mutex_unlock(&pgsql_lock);
01018       return -1;
01019    }
01020 
01021 
01022    /* Create the first part of the query using the first parameter/value pairs we just extracted
01023       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
01024 
01025    ESCAPE_STRING(buf1, keyfield);
01026    ESCAPE_STRING(buf2, lookup);
01027    ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
01028    while ((newparam = va_arg(ap, const char *))) {
01029       newval = va_arg(ap, const char *);
01030       ESCAPE_STRING(buf1, newparam);
01031       ESCAPE_STRING(buf2, newval);
01032       ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
01033    }
01034 
01035    ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
01036 
01037    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01038       ast_log(LOG_WARNING,
01039             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01040       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01041       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01042       ast_mutex_unlock(&pgsql_lock);
01043       return -1;
01044    } else {
01045       ExecStatusType result_status = PQresultStatus(result);
01046       if (result_status != PGRES_COMMAND_OK
01047          && result_status != PGRES_TUPLES_OK
01048          && result_status != PGRES_NONFATAL_ERROR) {
01049          ast_log(LOG_WARNING,
01050                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01051          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01052          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01053                   PQresultErrorMessage(result), PQresStatus(result_status));
01054          ast_mutex_unlock(&pgsql_lock);
01055          return -1;
01056       }
01057    }
01058 
01059    numrows = atoi(PQcmdTuples(result));
01060    ast_mutex_unlock(&pgsql_lock);
01061 
01062    ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01063 
01064    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
01065     * An integer greater than zero indicates the number of rows affected
01066     * Zero indicates that no records were updated
01067     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
01068     */
01069 
01070    if (numrows >= 0)
01071       return (int) numrows;
01072 
01073    return -1;
01074 }

static void destroy_table ( struct tables table  )  [static]

Definition at line 117 of file res_config_pgsql.c.

References ast_free, AST_LIST_REMOVE_HEAD, ast_rwlock_destroy, ast_rwlock_unlock, ast_rwlock_wrlock, tables::columns, and tables::lock.

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

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

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

Definition at line 291 of file res_config_pgsql.c.

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

Referenced by update2_pgsql(), and update_pgsql().

00292 {
00293    struct columns *column;
00294 
00295    /* Check that the column exists in the table */
00296    AST_LIST_TRAVERSE(&t->columns, column, list) {
00297       if (strcmp(column->name, colname) == 0) {
00298          return column;
00299       }
00300    }
00301    return NULL;
00302 }

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

Definition at line 129 of file res_config_pgsql.c.

References ast_alloca, 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_mutex_lock, ast_mutex_unlock, ast_rwlock_init, ast_rwlock_rdlock, ast_str_buffer(), ast_str_set(), ast_str_thread_get(), ast_strdupa, ast_strlen_zero(), ast_verb, tables::columns, destroy_table(), findtable_buf, has_schema_support, columns::hasdefault, columns::len, tables::lock, LOG_ERROR, columns::name, tables::name, columns::notnull, pgsql_lock, pgsql_reconnect(), RAII_VAR, table, and columns::type.

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

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

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

Definition at line 1556 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, ast_cli_args::n, columns::name, tables::name, columns::notnull, release_table, columns::type, ast_cli_entry::usage, and ast_cli_args::word.

01557 {
01558    struct tables *cur;
01559    int l, which;
01560    char *ret = NULL;
01561 
01562    switch (cmd) {
01563    case CLI_INIT:
01564       e->command = "realtime show pgsql cache";
01565       e->usage =
01566          "Usage: realtime show pgsql cache [<table>]\n"
01567          "       Shows table cache for the PostgreSQL RealTime driver\n";
01568       return NULL;
01569    case CLI_GENERATE:
01570       if (a->argc != 4) {
01571          return NULL;
01572       }
01573       l = strlen(a->word);
01574       which = 0;
01575       AST_LIST_LOCK(&psql_tables);
01576       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01577          if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01578             ret = ast_strdup(cur->name);
01579             break;
01580          }
01581       }
01582       AST_LIST_UNLOCK(&psql_tables);
01583       return ret;
01584    }
01585 
01586    if (a->argc == 4) {
01587       /* List of tables */
01588       AST_LIST_LOCK(&psql_tables);
01589       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01590          ast_cli(a->fd, "%s\n", cur->name);
01591       }
01592       AST_LIST_UNLOCK(&psql_tables);
01593    } else if (a->argc == 5) {
01594       /* List of columns */
01595       if ((cur = find_table(NULL, a->argv[4]))) {
01596          struct columns *col;
01597          ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01598          ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01599          AST_LIST_TRAVERSE(&cur->columns, col, list) {
01600             ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01601          }
01602          release_table(cur);
01603       } else {
01604          ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01605       }
01606    }
01607    return 0;
01608 }

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

Definition at line 1610 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.

01611 {
01612    char status[256], credentials[100] = "";
01613    int ctimesec = time(NULL) - connect_time;
01614 
01615    switch (cmd) {
01616    case CLI_INIT:
01617       e->command = "realtime show pgsql status";
01618       e->usage =
01619          "Usage: realtime show pgsql status\n"
01620          "       Shows connection information for the PostgreSQL RealTime driver\n";
01621       return NULL;
01622    case CLI_GENERATE:
01623       return NULL;
01624    }
01625 
01626    if (a->argc != 4)
01627       return CLI_SHOWUSAGE;
01628 
01629    if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01630       if (!ast_strlen_zero(dbhost))
01631          snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01632       else if (!ast_strlen_zero(dbsock))
01633          snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01634       else
01635          snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01636 
01637       if (!ast_strlen_zero(dbuser))
01638          snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01639 
01640       if (ctimesec > 31536000)
01641          ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01642                status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01643                (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01644       else if (ctimesec > 86400)
01645          ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01646                credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01647                ctimesec % 60);
01648       else if (ctimesec > 3600)
01649          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01650                ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01651       else if (ctimesec > 60)
01652          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01653                ctimesec % 60);
01654       else
01655          ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01656 
01657       return CLI_SUCCESS;
01658    } else {
01659       return CLI_FAILURE;
01660    }
01661 }

static int load_module ( void   )  [static]

Definition at line 1353 of file res_config_pgsql.c.

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

01354 {
01355    if(!parse_config(0))
01356       return AST_MODULE_LOAD_DECLINE;
01357 
01358    ast_config_engine_register(&pgsql_engine);
01359    ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01360    ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01361 
01362    return 0;
01363 }

static int parse_config ( int  reload  )  [static]

Definition at line 1399 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_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.

Referenced by load_module(), and reload().

01400 {
01401    struct ast_config *config;
01402    const char *s;
01403    struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01404 
01405    config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01406    if (config == CONFIG_STATUS_FILEUNCHANGED) {
01407       return 0;
01408    }
01409 
01410    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01411       ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01412       return 0;
01413    }
01414 
01415    ast_mutex_lock(&pgsql_lock);
01416 
01417    if (pgsqlConn) {
01418       PQfinish(pgsqlConn);
01419       pgsqlConn = NULL;
01420    }
01421 
01422    if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01423       ast_log(LOG_WARNING,
01424             "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01425       strcpy(dbuser, "asterisk");
01426    } else {
01427       ast_copy_string(dbuser, s, sizeof(dbuser));
01428    }
01429 
01430    if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01431       ast_log(LOG_WARNING,
01432             "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01433       strcpy(dbpass, "asterisk");
01434    } else {
01435       ast_copy_string(dbpass, s, sizeof(dbpass));
01436    }
01437 
01438    if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01439       ast_log(LOG_WARNING,
01440             "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01441       dbhost[0] = '\0';
01442    } else {
01443       ast_copy_string(dbhost, s, sizeof(dbhost));
01444    }
01445 
01446    if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01447       ast_log(LOG_WARNING,
01448             "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01449       strcpy(dbname, "asterisk");
01450    } else {
01451       ast_copy_string(dbname, s, sizeof(dbname));
01452    }
01453 
01454    if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01455       ast_log(LOG_WARNING,
01456             "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01457       dbport = 5432;
01458    } else {
01459       dbport = atoi(s);
01460    }
01461 
01462    if (!ast_strlen_zero(dbhost)) {
01463       /* No socket needed */
01464    } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01465       ast_log(LOG_WARNING,
01466             "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
01467       strcpy(dbsock, "/tmp");
01468    } else {
01469       ast_copy_string(dbsock, s, sizeof(dbsock));
01470    }
01471 
01472    if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01473       ast_log(LOG_WARNING,
01474             "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01475       requirements = RQ_WARN;
01476    } else if (!strcasecmp(s, "createclose")) {
01477       requirements = RQ_CREATECLOSE;
01478    } else if (!strcasecmp(s, "createchar")) {
01479       requirements = RQ_CREATECHAR;
01480    }
01481 
01482    ast_config_destroy(config);
01483 
01484    if (option_debug) {
01485       if (!ast_strlen_zero(dbhost)) {
01486          ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01487          ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01488       } else {
01489          ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01490       }
01491       ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01492       ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01493       ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01494    }
01495 
01496    if (!pgsql_reconnect(NULL)) {
01497       ast_log(LOG_WARNING,
01498             "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01499       ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01500    }
01501 
01502    ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01503 
01504    /* Done reloading. Release lock so others can now use driver. */
01505    ast_mutex_unlock(&pgsql_lock);
01506 
01507    return 1;
01508 }

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

Definition at line 1510 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(), find_table(), parse_config(), realtime_multi_pgsql(), realtime_pgsql(), require_pgsql(), store_pgsql(), update2_pgsql(), and update_pgsql().

01511 {
01512    char my_database[50];
01513 
01514    ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01515 
01516    /* mutex lock should have been locked before calling this function. */
01517 
01518    if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01519       PQfinish(pgsqlConn);
01520       pgsqlConn = NULL;
01521    }
01522 
01523    /* DB password can legitimately be 0-length */
01524    if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01525       struct ast_str *connInfo = ast_str_create(128);
01526 
01527       ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01528          S_OR(dbhost, dbsock), dbport, my_database, dbuser);
01529       if (!ast_strlen_zero(dbpass))
01530          ast_str_append(&connInfo, 0, " password=%s", dbpass);
01531 
01532       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01533       pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01534       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01535       ast_free(connInfo);
01536       connInfo = NULL;
01537 
01538       ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01539       if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01540          ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01541          connect_time = time(NULL);
01542          version = PQserverVersion(pgsqlConn);
01543          return 1;
01544       } else {
01545          ast_log(LOG_ERROR,
01546                "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01547                my_database, dbhost, PQresultErrorMessage(NULL));
01548          return 0;
01549       }
01550    } else {
01551       ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01552       return 1;
01553    }
01554 }

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

Definition at line 440 of file res_config_pgsql.c.

References ast_calloc, ast_category_append(), ast_category_new(), ast_category_rename(), ast_config_destroy(), 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(), dbname, ESCAPE_STRING, escapebuf_buf, LOG_ERROR, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RAII_VAR, sql_buf, and var.

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

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

Definition at line 304 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(), dbname, ESCAPE_STRING, escapebuf_buf, LOG_ERROR, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RAII_VAR, sql_buf, and var.

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

static int reload ( void   )  [static]

Definition at line 1392 of file res_config_pgsql.c.

References parse_config().

01393 {
01394    parse_config(1);
01395 
01396    return 0;
01397 }

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

Definition at line 1176 of file res_config_pgsql.c.

References ast_debug, ast_free, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_str_buffer(), ast_str_create(), ast_str_set(), tables::columns, dbname, find_table(), columns::len, 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.

01177 {
01178    struct columns *column;
01179    struct tables *table;
01180    char *elm;
01181    int type, size, res = 0;
01182 
01183    /*
01184     * Ignore database from the extconfig.conf since it was
01185     * configured by res_pgsql.conf.
01186     */
01187    database = dbname;
01188 
01189    table = find_table(database, tablename);
01190    if (!table) {
01191       ast_log(LOG_WARNING, "Table %s not found in database.  This table should exist if you're using realtime.\n", tablename);
01192       return -1;
01193    }
01194 
01195    while ((elm = va_arg(ap, char *))) {
01196       type = va_arg(ap, require_type);
01197       size = va_arg(ap, int);
01198       AST_LIST_TRAVERSE(&table->columns, column, list) {
01199          if (strcmp(column->name, elm) == 0) {
01200             /* Char can hold anything, as long as it is large enough */
01201             if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01202                if ((size > column->len) && column->len != -1) {
01203                   ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01204                   res = -1;
01205                }
01206             } else if (strncmp(column->type, "int", 3) == 0) {
01207                int typesize = atoi(column->type + 3);
01208                /* Integers can hold only other integers */
01209                if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01210                   type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01211                   type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01212                   type == RQ_UINTEGER2) && typesize == 2) {
01213                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01214                   res = -1;
01215                } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01216                   type == RQ_UINTEGER4) && typesize == 4) {
01217                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01218                   res = -1;
01219                } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01220                   ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01221                      column->name,
01222                         type == RQ_CHAR ? "char" :
01223                         type == RQ_DATETIME ? "datetime" :
01224                         type == RQ_DATE ? "date" :
01225                         type == RQ_FLOAT ? "float" :
01226                         "a rather stiff drink ",
01227                      size, column->type);
01228                   res = -1;
01229                }
01230             } else if (strncmp(column->type, "float", 5) == 0) {
01231                if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01232                   ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01233                   res = -1;
01234                }
01235             } else if (strncmp(column->type, "timestamp", 9) == 0) {
01236                if (type != RQ_DATETIME && type != RQ_DATE) {
01237                   ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01238                   res = -1;
01239                }
01240             } else { /* There are other types that no module implements yet */
01241                ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01242                res = -1;
01243             }
01244             break;
01245          }
01246       }
01247 
01248       if (!column) {
01249          if (requirements == RQ_WARN) {
01250             ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01251          } else {
01252             struct ast_str *sql = ast_str_create(100);
01253             char fieldtype[15];
01254             PGresult *result;
01255 
01256             if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01257                /* Size is minimum length; make it at least 50% greater,
01258                 * just to be sure, because PostgreSQL doesn't support
01259                 * resizing columns. */
01260                snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01261                   size < 15 ? size * 2 :
01262                   (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01263             } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01264                snprintf(fieldtype, sizeof(fieldtype), "INT2");
01265             } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01266                snprintf(fieldtype, sizeof(fieldtype), "INT4");
01267             } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01268                snprintf(fieldtype, sizeof(fieldtype), "INT8");
01269             } else if (type == RQ_UINTEGER8) {
01270                /* No such type on PostgreSQL */
01271                snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01272             } else if (type == RQ_FLOAT) {
01273                snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01274             } else if (type == RQ_DATE) {
01275                snprintf(fieldtype, sizeof(fieldtype), "DATE");
01276             } else if (type == RQ_DATETIME) {
01277                snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01278             } else {
01279                ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01280                ast_free(sql);
01281                continue;
01282             }
01283             ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01284             ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01285 
01286             ast_mutex_lock(&pgsql_lock);
01287             if (!pgsql_reconnect(database)) {
01288                ast_mutex_unlock(&pgsql_lock);
01289                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01290                ast_free(sql);
01291                continue;
01292             }
01293 
01294             ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01295             result = PQexec(pgsqlConn, ast_str_buffer(sql));
01296             ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01297             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01298                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01299             }
01300             PQclear(result);
01301             ast_mutex_unlock(&pgsql_lock);
01302 
01303             ast_free(sql);
01304          }
01305       }
01306    }
01307    release_table(table);
01308    return res;
01309 }

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

Definition at line 882 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(), dbname, ESCAPE_STRING, escapebuf_buf, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RAII_VAR, sql_buf, and where_buf.

00883 {
00884    RAII_VAR(PGresult *, result, NULL, PQclear);
00885    int numrows;
00886    struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
00887    struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
00888    struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
00889    int pgresult;
00890    const char *newparam, *newval;
00891 
00892    /*
00893     * Ignore database from the extconfig.conf since it was
00894     * configured by res_pgsql.conf.
00895     */
00896    database = dbname;
00897 
00898    if (!table) {
00899       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00900       return -1;
00901    }
00902 
00903    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00904    newparam = va_arg(ap, const char *);
00905    newval = va_arg(ap, const char *);
00906    if (!newparam || !newval) {
00907       ast_log(LOG_WARNING,
00908             "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
00909       if (pgsqlConn) {
00910          PQfinish(pgsqlConn);
00911          pgsqlConn = NULL;
00912       }
00913       return -1;
00914    }
00915 
00916    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
00917    ast_mutex_lock(&pgsql_lock);
00918    if (!pgsql_reconnect(database)) {
00919       ast_mutex_unlock(&pgsql_lock);
00920       return -1;
00921    }
00922 
00923    /* Create the first part of the query using the first parameter/value pairs we just extracted
00924       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00925    ESCAPE_STRING(buf, newparam);
00926    ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
00927    ESCAPE_STRING(buf, newval);
00928    ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
00929    while ((newparam = va_arg(ap, const char *))) {
00930       newval = va_arg(ap, const char *);
00931       ESCAPE_STRING(buf, newparam);
00932       ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
00933       ESCAPE_STRING(buf, newval);
00934       ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
00935    }
00936    ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00937 
00938    ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
00939 
00940    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql1)))) {
00941       ast_log(LOG_WARNING,
00942             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00943       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00944       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00945       ast_mutex_unlock(&pgsql_lock);
00946       return -1;
00947    } else {
00948       ExecStatusType result_status = PQresultStatus(result);
00949       if (result_status != PGRES_COMMAND_OK
00950          && result_status != PGRES_TUPLES_OK
00951          && result_status != PGRES_NONFATAL_ERROR) {
00952          ast_log(LOG_WARNING,
00953                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00954          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00955          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00956                   PQresultErrorMessage(result), PQresStatus(result_status));
00957          ast_mutex_unlock(&pgsql_lock);
00958          return -1;
00959       }
00960    }
00961 
00962    numrows = atoi(PQcmdTuples(result));
00963    ast_mutex_unlock(&pgsql_lock);
00964 
00965    ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.", table);
00966 
00967    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00968     * An integer greater than zero indicates the number of rows affected
00969     * Zero indicates that no records were updated
00970     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00971     */
00972 
00973    if (numrows >= 0) {
00974       return numrows;
00975    }
00976 
00977    return -1;
00978 }

static int unload_module ( void   )  [static]

Definition at line 1365 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, destroy_table(), pgsql_lock, and table.

01366 {
01367    struct tables *table;
01368    /* Acquire control before doing anything to the module itself. */
01369    ast_mutex_lock(&pgsql_lock);
01370 
01371    if (pgsqlConn) {
01372       PQfinish(pgsqlConn);
01373       pgsqlConn = NULL;
01374    }
01375    ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01376    ast_config_engine_deregister(&pgsql_engine);
01377    ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01378 
01379    /* Destroy cached table info */
01380    AST_LIST_LOCK(&psql_tables);
01381    while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01382       destroy_table(table);
01383    }
01384    AST_LIST_UNLOCK(&psql_tables);
01385 
01386    /* Unlock so something else can destroy the lock. */
01387    ast_mutex_unlock(&pgsql_lock);
01388 
01389    return 0;
01390 }

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

Definition at line 1311 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, dbname, destroy_table(), and tables::name.

01312 {
01313    struct tables *cur;
01314 
01315    /*
01316     * Ignore database from the extconfig.conf since it was
01317     * configured by res_pgsql.conf.
01318     */
01319    database = dbname;
01320 
01321    ast_debug(2, "About to lock table cache list\n");
01322    AST_LIST_LOCK(&psql_tables);
01323    ast_debug(2, "About to traverse table cache list\n");
01324    AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01325       if (strcmp(cur->name, tablename) == 0) {
01326          ast_debug(2, "About to remove matching cache entry\n");
01327          AST_LIST_REMOVE_CURRENT(list);
01328          ast_debug(2, "About to destroy matching cache entry\n");
01329          destroy_table(cur);
01330          ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01331          break;
01332       }
01333    }
01334    AST_LIST_TRAVERSE_SAFE_END
01335    AST_LIST_UNLOCK(&psql_tables);
01336    ast_debug(2, "About to return\n");
01337    return cur ? 0 : -1;
01338 }

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

Definition at line 745 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(), dbname, ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), first, LOG_ERROR, LOG_NOTICE, LOG_WARNING, pgsql_lock, pgsql_reconnect(), RAII_VAR, release_table, sql_buf, table, and where_buf.

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

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

Definition at line 605 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(), tables::columns, dbname, ESCAPE_STRING, escapebuf_buf, find_column(), find_table(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, pgsql_lock, pgsql_reconnect(), RAII_VAR, release_table, sql_buf, and table.

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


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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_REALTIME_DRIVER, } [static]

Definition at line 1669 of file res_config_pgsql.c.

Definition at line 1669 of file res_config_pgsql.c.

struct ast_cli_entry cli_realtime[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_realtime_pgsql_status, "Shows connection information for the PostgreSQL RealTime driver"),
   AST_CLI_DEFINE(handle_cli_realtime_pgsql_cache, "Shows cached tables within the PostgreSQL realtime driver"),
}

Definition at line 93 of file res_config_pgsql.c.

time_t connect_time = 0 [static]

Definition at line 84 of file res_config_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), and pgsql_reconnect().

char dbhost[MAX_DB_OPTION_SIZE] = "" [static]
char dbname[MAX_DB_OPTION_SIZE] = "" [static]
char dbpass[MAX_DB_OPTION_SIZE] = "" [static]

Definition at line 80 of file res_config_pgsql.c.

Referenced by parse_config(), and pgsql_reconnect().

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]

Definition at line 47 of file res_config_pgsql.c.

Referenced by find_table().

Definition at line 1340 of file res_config_pgsql.c.

ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]
PGconn* pgsqlConn = NULL [static]

Definition at line 54 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 50 of file res_config_pgsql.c.

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

Definition at line 55 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 48 of file res_config_pgsql.c.

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


Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1