Mon Sep 20 2010 00:23:36

Asterisk developer's documentation


res_config_pgsql.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Copyright (C) 1999-2005, Digium, Inc.
00005  * 
00006  * Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
00007  * Mark Spencer <markster@digium.com>  - Asterisk Author
00008  * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
00009  *
00010  * res_config_pgsql.c <PostgreSQL plugin for RealTime configuration engine>
00011  *
00012  * v1.0   - (07-11-05) - Initial version based on res_config_mysql v2.0
00013  */
00014 
00015 /*! \file
00016  *
00017  * \brief PostgreSQL plugin for Asterisk RealTime Architecture
00018  *
00019  * \author Mark Spencer <markster@digium.com>
00020  * \author Manuel Guesdon <mguesdon@oxymium.net> - PostgreSQL RealTime Driver Author/Adaptor
00021  *
00022  * \arg http://www.postgresql.org
00023  */
00024 
00025 /*** MODULEINFO
00026    <depend>pgsql</depend>
00027  ***/
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 229094 $")
00032 
00033 #include <libpq-fe.h>         /* PostgreSQL */
00034 
00035 #include "asterisk/file.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/config.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/lock.h"
00041 #include "asterisk/utils.h"
00042 #include "asterisk/cli.h"
00043 
00044 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00045 AST_THREADSTORAGE(sql_buf);
00046 AST_THREADSTORAGE(findtable_buf);
00047 AST_THREADSTORAGE(where_buf);
00048 AST_THREADSTORAGE(escapebuf_buf);
00049 
00050 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
00051 
00052 PGconn *pgsqlConn = NULL;
00053 static int version;
00054 #define has_schema_support (version > 70300 ? 1 : 0)
00055 
00056 #define MAX_DB_OPTION_SIZE 64
00057 
00058 struct columns {
00059    char *name;
00060    char *type;
00061    int len;
00062    unsigned int notnull:1;
00063    unsigned int hasdefault:1;
00064    AST_LIST_ENTRY(columns) list;
00065 };
00066 
00067 struct tables {
00068    ast_rwlock_t lock;
00069    AST_LIST_HEAD_NOLOCK(psql_columns, columns) columns;
00070    AST_LIST_ENTRY(tables) list;
00071    char name[0];
00072 };
00073 
00074 static AST_LIST_HEAD_STATIC(psql_tables, tables);
00075 
00076 static char dbhost[MAX_DB_OPTION_SIZE] = "";
00077 static char dbuser[MAX_DB_OPTION_SIZE] = "";
00078 static char dbpass[MAX_DB_OPTION_SIZE] = "";
00079 static char dbname[MAX_DB_OPTION_SIZE] = "";
00080 static char dbsock[MAX_DB_OPTION_SIZE] = "";
00081 static int dbport = 5432;
00082 static time_t connect_time = 0;
00083 
00084 static int parse_config(int reload);
00085 static int pgsql_reconnect(const char *database);
00086 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00087 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00088 
00089 enum { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR } requirements;
00090 
00091 static struct ast_cli_entry cli_realtime[] = {
00092    AST_CLI_DEFINE(handle_cli_realtime_pgsql_status, "Shows connection information for the PostgreSQL RealTime driver"),
00093    AST_CLI_DEFINE(handle_cli_realtime_pgsql_cache, "Shows cached tables within the PostgreSQL realtime driver"),
00094 };
00095 
00096 #define ESCAPE_STRING(buffer, stringname) \
00097    do { \
00098       int len; \
00099       if ((len = strlen(stringname)) > (ast_str_size(buffer) - 1) / 2) { \
00100          ast_str_make_space(&buffer, len * 2 + 1); \
00101       } \
00102       PQescapeStringConn(pgsqlConn, ast_str_buffer(buffer), stringname, len, &pgresult); \
00103    } while (0)
00104 
00105 static void destroy_table(struct tables *table)
00106 {
00107    struct columns *column;
00108    ast_rwlock_wrlock(&table->lock);
00109    while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
00110       ast_free(column);
00111    }
00112    ast_rwlock_unlock(&table->lock);
00113    ast_rwlock_destroy(&table->lock);
00114    ast_free(table);
00115 }
00116 
00117 static struct tables *find_table(const char *orig_tablename)
00118 {
00119    struct columns *column;
00120    struct tables *table;
00121    struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
00122    char *pgerror;
00123    PGresult *result;
00124    char *fname, *ftype, *flen, *fnotnull, *fdef;
00125    int i, rows;
00126 
00127    AST_LIST_LOCK(&psql_tables);
00128    AST_LIST_TRAVERSE(&psql_tables, table, list) {
00129       if (!strcasecmp(table->name, orig_tablename)) {
00130          ast_debug(1, "Found table in cache; now locking\n");
00131          ast_rwlock_rdlock(&table->lock);
00132          ast_debug(1, "Lock cached table; now returning\n");
00133          AST_LIST_UNLOCK(&psql_tables);
00134          return table;
00135       }
00136    }
00137 
00138    ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
00139 
00140    /* Not found, scan the table */
00141    if (has_schema_support) {
00142       char *schemaname, *tablename;
00143       if (strchr(orig_tablename, '.')) {
00144          schemaname = ast_strdupa(orig_tablename);
00145          tablename = strchr(schemaname, '.');
00146          *tablename++ = '\0';
00147       } else {
00148          schemaname = "";
00149          tablename = ast_strdupa(orig_tablename);
00150       }
00151 
00152       /* Escape special characters in schemaname */
00153       if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00154          char *tmp = schemaname, *ptr;
00155 
00156          ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00157          for (; *tmp; tmp++) {
00158             if (strchr("\\'", *tmp)) {
00159                *ptr++ = *tmp;
00160             }
00161             *ptr++ = *tmp;
00162          }
00163          *ptr = '\0';
00164       }
00165       /* Escape special characters in tablename */
00166       if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00167          char *tmp = tablename, *ptr;
00168 
00169          ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00170          for (; *tmp; tmp++) {
00171             if (strchr("\\'", *tmp)) {
00172                *ptr++ = *tmp;
00173             }
00174             *ptr++ = *tmp;
00175          }
00176          *ptr = '\0';
00177       }
00178 
00179       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",
00180          tablename,
00181          ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00182    } else {
00183       /* Escape special characters in tablename */
00184       if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
00185          const char *tmp = orig_tablename;
00186          char *ptr;
00187 
00188          orig_tablename = ptr = alloca(strlen(tmp) * 2 + 1);
00189          for (; *tmp; tmp++) {
00190             if (strchr("\\'", *tmp)) {
00191                *ptr++ = *tmp;
00192             }
00193             *ptr++ = *tmp;
00194          }
00195          *ptr = '\0';
00196       }
00197 
00198       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);
00199    }
00200 
00201    result = PQexec(pgsqlConn, ast_str_buffer(sql));
00202    ast_debug(1, "Query of table structure complete.  Now retrieving results.\n");
00203    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00204       pgerror = PQresultErrorMessage(result);
00205       ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00206       PQclear(result);
00207       AST_LIST_UNLOCK(&psql_tables);
00208       return NULL;
00209    }
00210 
00211    if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
00212       ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
00213       AST_LIST_UNLOCK(&psql_tables);
00214       return NULL;
00215    }
00216    strcpy(table->name, orig_tablename); /* SAFE */
00217    ast_rwlock_init(&table->lock);
00218    AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
00219 
00220    rows = PQntuples(result);
00221    for (i = 0; i < rows; i++) {
00222       fname = PQgetvalue(result, i, 0);
00223       ftype = PQgetvalue(result, i, 1);
00224       flen = PQgetvalue(result, i, 2);
00225       fnotnull = PQgetvalue(result, i, 3);
00226       fdef = PQgetvalue(result, i, 4);
00227       ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00228 
00229       if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
00230          ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
00231          destroy_table(table);
00232          AST_LIST_UNLOCK(&psql_tables);
00233          return NULL;
00234       }
00235 
00236       if (strcmp(flen, "-1") == 0) {
00237          /* Some types, like chars, have the length stored in a different field */
00238          flen = PQgetvalue(result, i, 5);
00239          sscanf(flen, "%30d", &column->len);
00240          column->len -= 4;
00241       } else {
00242          sscanf(flen, "%30d", &column->len);
00243       }
00244       column->name = (char *)column + sizeof(*column);
00245       column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
00246       strcpy(column->name, fname);
00247       strcpy(column->type, ftype);
00248       if (*fnotnull == 't') {
00249          column->notnull = 1;
00250       } else {
00251          column->notnull = 0;
00252       }
00253       if (!ast_strlen_zero(fdef)) {
00254          column->hasdefault = 1;
00255       } else {
00256          column->hasdefault = 0;
00257       }
00258       AST_LIST_INSERT_TAIL(&table->columns, column, list);
00259    }
00260    PQclear(result);
00261 
00262    AST_LIST_INSERT_TAIL(&psql_tables, table, list);
00263    ast_rwlock_rdlock(&table->lock);
00264    AST_LIST_UNLOCK(&psql_tables);
00265    return table;
00266 }
00267 
00268 #define release_table(table) ast_rwlock_unlock(&(table)->lock);
00269 
00270 static struct columns *find_column(struct tables *t, const char *colname)
00271 {
00272    struct columns *column;
00273 
00274    /* Check that the column exists in the table */
00275    AST_LIST_TRAVERSE(&t->columns, column, list) {
00276       if (strcmp(column->name, colname) == 0) {
00277          return column;
00278       }
00279    }
00280    return NULL;
00281 }
00282 
00283 static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap)
00284 {
00285    PGresult *result = NULL;
00286    int num_rows = 0, pgresult;
00287    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00288    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00289    char *stringp;
00290    char *chunk;
00291    char *op;
00292    const char *newparam, *newval;
00293    struct ast_variable *var = NULL, *prev = NULL;
00294 
00295    if (!tablename) {
00296       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00297       return NULL;
00298    }
00299 
00300    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00301    newparam = va_arg(ap, const char *);
00302    newval = va_arg(ap, const char *);
00303    if (!newparam || !newval) {
00304       ast_log(LOG_WARNING,
00305             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00306       if (pgsqlConn) {
00307          PQfinish(pgsqlConn);
00308          pgsqlConn = NULL;
00309       }
00310       return NULL;
00311    }
00312 
00313    /* Create the first part of the query using the first parameter/value pairs we just extracted
00314       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00315    op = strchr(newparam, ' ') ? "" : " =";
00316 
00317    ESCAPE_STRING(escapebuf, newval);
00318    if (pgresult) {
00319       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00320       va_end(ap);
00321       return NULL;
00322    }
00323 
00324    ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
00325    while ((newparam = va_arg(ap, const char *))) {
00326       newval = va_arg(ap, const char *);
00327       if (!strchr(newparam, ' '))
00328          op = " =";
00329       else
00330          op = "";
00331 
00332       ESCAPE_STRING(escapebuf, newval);
00333       if (pgresult) {
00334          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00335          va_end(ap);
00336          return NULL;
00337       }
00338 
00339       ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00340    }
00341    va_end(ap);
00342 
00343    /* We now have our complete statement; Lets connect to the server and execute it. */
00344    ast_mutex_lock(&pgsql_lock);
00345    if (!pgsql_reconnect(database)) {
00346       ast_mutex_unlock(&pgsql_lock);
00347       return NULL;
00348    }
00349 
00350    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00351       ast_log(LOG_WARNING,
00352             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
00353       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00354       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00355       ast_mutex_unlock(&pgsql_lock);
00356       return NULL;
00357    } else {
00358       ExecStatusType result_status = PQresultStatus(result);
00359       if (result_status != PGRES_COMMAND_OK
00360          && result_status != PGRES_TUPLES_OK
00361          && result_status != PGRES_NONFATAL_ERROR) {
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 (%s)\n",
00366                   PQresultErrorMessage(result), PQresStatus(result_status));
00367          ast_mutex_unlock(&pgsql_lock);
00368          return NULL;
00369       }
00370    }
00371 
00372    ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00373 
00374    if ((num_rows = PQntuples(result)) > 0) {
00375       int i = 0;
00376       int rowIndex = 0;
00377       int numFields = PQnfields(result);
00378       char **fieldnames = NULL;
00379 
00380       ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00381 
00382       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00383          ast_mutex_unlock(&pgsql_lock);
00384          PQclear(result);
00385          return NULL;
00386       }
00387       for (i = 0; i < numFields; i++)
00388          fieldnames[i] = PQfname(result, i);
00389       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00390          for (i = 0; i < numFields; i++) {
00391             stringp = PQgetvalue(result, rowIndex, i);
00392             while (stringp) {
00393                chunk = strsep(&stringp, ";");
00394                if (!ast_strlen_zero(ast_strip(chunk))) {
00395                   if (prev) {
00396                      prev->next = ast_variable_new(fieldnames[i], chunk, "");
00397                      if (prev->next) {
00398                         prev = prev->next;
00399                      }
00400                   } else {
00401                      prev = var = ast_variable_new(fieldnames[i], chunk, "");
00402                   }
00403                }
00404             }
00405          }
00406       }
00407       ast_free(fieldnames);
00408    } else {
00409       ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
00410    }
00411 
00412    ast_mutex_unlock(&pgsql_lock);
00413    PQclear(result);
00414 
00415    return var;
00416 }
00417 
00418 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
00419 {
00420    PGresult *result = NULL;
00421    int num_rows = 0, pgresult;
00422    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00423    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00424    const char *initfield = NULL;
00425    char *stringp;
00426    char *chunk;
00427    char *op;
00428    const char *newparam, *newval;
00429    struct ast_variable *var = NULL;
00430    struct ast_config *cfg = NULL;
00431    struct ast_category *cat = NULL;
00432 
00433    if (!table) {
00434       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00435       return NULL;
00436    }
00437 
00438    if (!(cfg = ast_config_new()))
00439       return NULL;
00440 
00441    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00442    newparam = va_arg(ap, const char *);
00443    newval = va_arg(ap, const char *);
00444    if (!newparam || !newval) {
00445       ast_log(LOG_WARNING,
00446             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00447       if (pgsqlConn) {
00448          PQfinish(pgsqlConn);
00449          pgsqlConn = NULL;
00450       }
00451       return NULL;
00452    }
00453 
00454    initfield = ast_strdupa(newparam);
00455    if ((op = strchr(initfield, ' '))) {
00456       *op = '\0';
00457    }
00458 
00459    /* Create the first part of the query using the first parameter/value pairs we just extracted
00460       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00461 
00462    if (!strchr(newparam, ' '))
00463       op = " =";
00464    else
00465       op = "";
00466 
00467    ESCAPE_STRING(escapebuf, newval);
00468    if (pgresult) {
00469       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00470       va_end(ap);
00471       return NULL;
00472    }
00473 
00474    ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
00475    while ((newparam = va_arg(ap, const char *))) {
00476       newval = va_arg(ap, const char *);
00477       if (!strchr(newparam, ' '))
00478          op = " =";
00479       else
00480          op = "";
00481 
00482       ESCAPE_STRING(escapebuf, newval);
00483       if (pgresult) {
00484          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00485          va_end(ap);
00486          return NULL;
00487       }
00488 
00489       ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00490    }
00491 
00492    if (initfield) {
00493       ast_str_append(&sql, 0, " ORDER BY %s", initfield);
00494    }
00495 
00496    va_end(ap);
00497 
00498    /* We now have our complete statement; Lets connect to the server and execute it. */
00499    ast_mutex_lock(&pgsql_lock);
00500    if (!pgsql_reconnect(database)) {
00501       ast_mutex_unlock(&pgsql_lock);
00502       return NULL;
00503    }
00504 
00505    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00506       ast_log(LOG_WARNING,
00507             "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00508       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00509       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00510       ast_mutex_unlock(&pgsql_lock);
00511       return NULL;
00512    } else {
00513       ExecStatusType result_status = PQresultStatus(result);
00514       if (result_status != PGRES_COMMAND_OK
00515          && result_status != PGRES_TUPLES_OK
00516          && result_status != PGRES_NONFATAL_ERROR) {
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 (%s)\n",
00521                   PQresultErrorMessage(result), PQresStatus(result_status));
00522          ast_mutex_unlock(&pgsql_lock);
00523          return NULL;
00524       }
00525    }
00526 
00527    ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00528 
00529    if ((num_rows = PQntuples(result)) > 0) {
00530       int numFields = PQnfields(result);
00531       int i = 0;
00532       int rowIndex = 0;
00533       char **fieldnames = NULL;
00534 
00535       ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00536 
00537       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00538          ast_mutex_unlock(&pgsql_lock);
00539          PQclear(result);
00540          return NULL;
00541       }
00542       for (i = 0; i < numFields; i++)
00543          fieldnames[i] = PQfname(result, i);
00544 
00545       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00546          var = NULL;
00547          if (!(cat = ast_category_new("","",99999)))
00548             continue;
00549          for (i = 0; i < numFields; i++) {
00550             stringp = PQgetvalue(result, rowIndex, i);
00551             while (stringp) {
00552                chunk = strsep(&stringp, ";");
00553                if (!ast_strlen_zero(ast_strip(chunk))) {
00554                   if (initfield && !strcmp(initfield, fieldnames[i])) {
00555                      ast_category_rename(cat, chunk);
00556                   }
00557                   var = ast_variable_new(fieldnames[i], chunk, "");
00558                   ast_variable_append(cat, var);
00559                }
00560             }
00561          }
00562          ast_category_append(cfg, cat);
00563       }
00564       ast_free(fieldnames);
00565    } else {
00566       ast_log(LOG_WARNING,
00567             "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
00568    }
00569 
00570    ast_mutex_unlock(&pgsql_lock);
00571    PQclear(result);
00572 
00573    return cfg;
00574 }
00575 
00576 static int update_pgsql(const char *database, const char *tablename, const char *keyfield,
00577                   const char *lookup, va_list ap)
00578 {
00579    PGresult *result = NULL;
00580    int numrows = 0, pgresult;
00581    const char *newparam, *newval;
00582    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00583    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00584    struct tables *table;
00585    struct columns *column = NULL;
00586 
00587    if (!tablename) {
00588       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00589       return -1;
00590    }
00591 
00592    if (!(table = find_table(tablename))) {
00593       ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00594       return -1;
00595    }
00596 
00597    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00598    newparam = va_arg(ap, const char *);
00599    newval = va_arg(ap, const char *);
00600    if (!newparam || !newval) {
00601       ast_log(LOG_WARNING,
00602             "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00603       if (pgsqlConn) {
00604          PQfinish(pgsqlConn);
00605          pgsqlConn = NULL;
00606       }
00607       release_table(table);
00608       return -1;
00609    }
00610 
00611    /* Check that the column exists in the table */
00612    AST_LIST_TRAVERSE(&table->columns, column, list) {
00613       if (strcmp(column->name, newparam) == 0) {
00614          break;
00615       }
00616    }
00617 
00618    if (!column) {
00619       ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
00620       release_table(table);
00621       return -1;
00622    }
00623 
00624    /* Create the first part of the query using the first parameter/value pairs we just extracted
00625       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00626 
00627    ESCAPE_STRING(escapebuf, newval);
00628    if (pgresult) {
00629       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00630       va_end(ap);
00631       release_table(table);
00632       return -1;
00633    }
00634    ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
00635 
00636    while ((newparam = va_arg(ap, const char *))) {
00637       newval = va_arg(ap, const char *);
00638 
00639       if (!find_column(table, newparam)) {
00640          ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
00641          continue;
00642       }
00643 
00644       ESCAPE_STRING(escapebuf, newval);
00645       if (pgresult) {
00646          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00647          va_end(ap);
00648          release_table(table);
00649          return -1;
00650       }
00651 
00652       ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
00653    }
00654    va_end(ap);
00655    release_table(table);
00656 
00657    ESCAPE_STRING(escapebuf, lookup);
00658    if (pgresult) {
00659       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
00660       va_end(ap);
00661       return -1;
00662    }
00663 
00664    ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
00665 
00666    ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00667 
00668    /* We now have our complete statement; Lets connect to the server and execute it. */
00669    ast_mutex_lock(&pgsql_lock);
00670    if (!pgsql_reconnect(database)) {
00671       ast_mutex_unlock(&pgsql_lock);
00672       return -1;
00673    }
00674 
00675    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00676       ast_log(LOG_WARNING,
00677             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00678       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00679       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00680       ast_mutex_unlock(&pgsql_lock);
00681       return -1;
00682    } else {
00683       ExecStatusType result_status = PQresultStatus(result);
00684       if (result_status != PGRES_COMMAND_OK
00685          && result_status != PGRES_TUPLES_OK
00686          && result_status != PGRES_NONFATAL_ERROR) {
00687          ast_log(LOG_WARNING,
00688                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00689          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00690          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00691                   PQresultErrorMessage(result), PQresStatus(result_status));
00692          ast_mutex_unlock(&pgsql_lock);
00693          return -1;
00694       }
00695    }
00696 
00697    numrows = atoi(PQcmdTuples(result));
00698    ast_mutex_unlock(&pgsql_lock);
00699 
00700    ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00701 
00702    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00703     * An integer greater than zero indicates the number of rows affected
00704     * Zero indicates that no records were updated
00705     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00706     */
00707 
00708    if (numrows >= 0)
00709       return (int) numrows;
00710 
00711    return -1;
00712 }
00713 
00714 static int update2_pgsql(const char *database, const char *tablename, va_list ap)
00715 {
00716    PGresult *result = NULL;
00717    int numrows = 0, pgresult, first = 1;
00718    struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
00719    const char *newparam, *newval;
00720    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00721    struct ast_str *where = ast_str_thread_get(&where_buf, 100);
00722    struct tables *table;
00723 
00724    if (!tablename) {
00725       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00726       return -1;
00727    }
00728 
00729    if (!escapebuf || !sql || !where) {
00730       /* Memory error, already handled */
00731       return -1;
00732    }
00733 
00734    if (!(table = find_table(tablename))) {
00735       ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00736       return -1;
00737    }
00738 
00739    ast_str_set(&sql, 0, "UPDATE %s SET ", tablename);
00740    ast_str_set(&where, 0, "WHERE");
00741 
00742    while ((newparam = va_arg(ap, const char *))) {
00743       if (!find_column(table, newparam)) {
00744          ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
00745          release_table(table);
00746          return -1;
00747       }
00748          
00749       newval = va_arg(ap, const char *);
00750       ESCAPE_STRING(escapebuf, newval);
00751       if (pgresult) {
00752          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00753          release_table(table);
00754          ast_free(sql);
00755          return -1;
00756       }
00757       ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
00758       first = 0;
00759    }
00760 
00761    if (first) {
00762       ast_log(LOG_WARNING,
00763             "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
00764       if (pgsqlConn) {
00765          PQfinish(pgsqlConn);
00766          pgsqlConn = NULL;
00767       }
00768       release_table(table);
00769       return -1;
00770    }
00771 
00772    /* Now retrieve the columns to update */
00773    first = 1;
00774    while ((newparam = va_arg(ap, const char *))) {
00775       newval = va_arg(ap, const char *);
00776 
00777       /* If the column is not within the table, then skip it */
00778       if (!find_column(table, newparam)) {
00779          ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
00780          continue;
00781       }
00782 
00783       ESCAPE_STRING(escapebuf, newval);
00784       if (pgresult) {
00785          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00786          release_table(table);
00787          ast_free(sql);
00788          return -1;
00789       }
00790 
00791       ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
00792    }
00793    release_table(table);
00794 
00795    ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
00796 
00797    ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00798 
00799    /* We now have our complete statement; connect to the server and execute it. */
00800    ast_mutex_lock(&pgsql_lock);
00801    if (!pgsql_reconnect(database)) {
00802       ast_mutex_unlock(&pgsql_lock);
00803       return -1;
00804    }
00805 
00806    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00807       ast_log(LOG_WARNING,
00808             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00809       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00810       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00811       ast_mutex_unlock(&pgsql_lock);
00812       return -1;
00813    } else {
00814       ExecStatusType result_status = PQresultStatus(result);
00815       if (result_status != PGRES_COMMAND_OK
00816          && result_status != PGRES_TUPLES_OK
00817          && result_status != PGRES_NONFATAL_ERROR) {
00818          ast_log(LOG_WARNING,
00819                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00820          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00821          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00822                   PQresultErrorMessage(result), PQresStatus(result_status));
00823          ast_mutex_unlock(&pgsql_lock);
00824          return -1;
00825       }
00826    }
00827 
00828    numrows = atoi(PQcmdTuples(result));
00829    ast_mutex_unlock(&pgsql_lock);
00830 
00831    ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00832 
00833    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00834     * An integer greater than zero indicates the number of rows affected
00835     * Zero indicates that no records were updated
00836     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00837     */
00838 
00839    if (numrows >= 0) {
00840       return (int) numrows;
00841    }
00842 
00843    return -1;
00844 }
00845 
00846 static int store_pgsql(const char *database, const char *table, va_list ap)
00847 {
00848    PGresult *result = NULL;
00849    Oid insertid;
00850    struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
00851    struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
00852    struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
00853    int pgresult;
00854    const char *newparam, *newval;
00855 
00856    if (!table) {
00857       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00858       return -1;
00859    }
00860 
00861    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00862    newparam = va_arg(ap, const char *);
00863    newval = va_arg(ap, const char *);
00864    if (!newparam || !newval) {
00865       ast_log(LOG_WARNING,
00866             "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
00867       if (pgsqlConn) {
00868          PQfinish(pgsqlConn);
00869          pgsqlConn = NULL;
00870       }
00871       return -1;
00872    }
00873 
00874    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
00875    ast_mutex_lock(&pgsql_lock);
00876    if (!pgsql_reconnect(database)) {
00877       ast_mutex_unlock(&pgsql_lock);
00878       return -1;
00879    }
00880 
00881    /* Create the first part of the query using the first parameter/value pairs we just extracted
00882       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00883    ESCAPE_STRING(buf, newparam);
00884    ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
00885    ESCAPE_STRING(buf, newval);
00886    ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
00887    while ((newparam = va_arg(ap, const char *))) {
00888       newval = va_arg(ap, const char *);
00889       ESCAPE_STRING(buf, newparam);
00890       ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
00891       ESCAPE_STRING(buf, newval);
00892       ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
00893    }
00894    va_end(ap);
00895    ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00896 
00897    ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
00898 
00899    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql1)))) {
00900       ast_log(LOG_WARNING,
00901             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00902       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00903       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00904       ast_mutex_unlock(&pgsql_lock);
00905       return -1;
00906    } else {
00907       ExecStatusType result_status = PQresultStatus(result);
00908       if (result_status != PGRES_COMMAND_OK
00909          && result_status != PGRES_TUPLES_OK
00910          && result_status != PGRES_NONFATAL_ERROR) {
00911          ast_log(LOG_WARNING,
00912                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00913          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
00914          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00915                   PQresultErrorMessage(result), PQresStatus(result_status));
00916          ast_mutex_unlock(&pgsql_lock);
00917          return -1;
00918       }
00919    }
00920 
00921    insertid = PQoidValue(result);
00922    ast_mutex_unlock(&pgsql_lock);
00923 
00924    ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
00925 
00926    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00927     * An integer greater than zero indicates the number of rows affected
00928     * Zero indicates that no records were updated
00929     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00930     */
00931 
00932    if (insertid >= 0)
00933       return (int) insertid;
00934 
00935    return -1;
00936 }
00937 
00938 static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00939 {
00940    PGresult *result = NULL;
00941    int numrows = 0;
00942    int pgresult;
00943    struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
00944    struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
00945    const char *newparam, *newval;
00946 
00947    if (!table) {
00948       ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00949       return -1;
00950    }
00951 
00952    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00953    /*newparam = va_arg(ap, const char *);
00954    newval = va_arg(ap, const char *);
00955    if (!newparam || !newval) {*/
00956    if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup))  {
00957       ast_log(LOG_WARNING,
00958             "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
00959       if (pgsqlConn) {
00960          PQfinish(pgsqlConn);
00961          pgsqlConn = NULL;
00962       };
00963       return -1;
00964    }
00965 
00966    /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
00967    ast_mutex_lock(&pgsql_lock);
00968    if (!pgsql_reconnect(database)) {
00969       ast_mutex_unlock(&pgsql_lock);
00970       return -1;
00971    }
00972 
00973 
00974    /* Create the first part of the query using the first parameter/value pairs we just extracted
00975       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00976 
00977    ESCAPE_STRING(buf1, keyfield);
00978    ESCAPE_STRING(buf2, lookup);
00979    ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
00980    while ((newparam = va_arg(ap, const char *))) {
00981       newval = va_arg(ap, const char *);
00982       ESCAPE_STRING(buf1, newparam);
00983       ESCAPE_STRING(buf2, newval);
00984       ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
00985    }
00986    va_end(ap);
00987 
00988    ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
00989 
00990    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
00991       ast_log(LOG_WARNING,
00992             "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00993       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00994       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
00995       ast_mutex_unlock(&pgsql_lock);
00996       return -1;
00997    } else {
00998       ExecStatusType result_status = PQresultStatus(result);
00999       if (result_status != PGRES_COMMAND_OK
01000          && result_status != PGRES_TUPLES_OK
01001          && result_status != PGRES_NONFATAL_ERROR) {
01002          ast_log(LOG_WARNING,
01003                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01004          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01005          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01006                   PQresultErrorMessage(result), PQresStatus(result_status));
01007          ast_mutex_unlock(&pgsql_lock);
01008          return -1;
01009       }
01010    }
01011 
01012    numrows = atoi(PQcmdTuples(result));
01013    ast_mutex_unlock(&pgsql_lock);
01014 
01015    ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01016 
01017    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
01018     * An integer greater than zero indicates the number of rows affected
01019     * Zero indicates that no records were updated
01020     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
01021     */
01022 
01023    if (numrows >= 0)
01024       return (int) numrows;
01025 
01026    return -1;
01027 }
01028 
01029 
01030 static struct ast_config *config_pgsql(const char *database, const char *table,
01031                               const char *file, struct ast_config *cfg,
01032                               struct ast_flags flags, const char *suggested_incl, const char *who_asked)
01033 {
01034    PGresult *result = NULL;
01035    long num_rows;
01036    struct ast_variable *new_v;
01037    struct ast_category *cur_cat = NULL;
01038    struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01039    char last[80] = "";
01040    int last_cat_metric = 0;
01041 
01042    last[0] = '\0';
01043 
01044    if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01045       ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01046       return NULL;
01047    }
01048 
01049    ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01050          "WHERE filename='%s' and commented=0"
01051          "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01052 
01053    ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01054 
01055    /* We now have our complete statement; Lets connect to the server and execute it. */
01056    ast_mutex_lock(&pgsql_lock);
01057    if (!pgsql_reconnect(database)) {
01058       ast_mutex_unlock(&pgsql_lock);
01059       return NULL;
01060    }
01061 
01062    if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
01063       ast_log(LOG_WARNING,
01064             "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", table, database);
01065       ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01066       ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
01067       ast_mutex_unlock(&pgsql_lock);
01068       return NULL;
01069    } else {
01070       ExecStatusType result_status = PQresultStatus(result);
01071       if (result_status != PGRES_COMMAND_OK
01072          && result_status != PGRES_TUPLES_OK
01073          && result_status != PGRES_NONFATAL_ERROR) {
01074          ast_log(LOG_WARNING,
01075                "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
01076          ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
01077          ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
01078                   PQresultErrorMessage(result), PQresStatus(result_status));
01079          ast_mutex_unlock(&pgsql_lock);
01080          return NULL;
01081       }
01082    }
01083 
01084    if ((num_rows = PQntuples(result)) > 0) {
01085       int rowIndex = 0;
01086 
01087       ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01088 
01089       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01090          char *field_category = PQgetvalue(result, rowIndex, 0);
01091          char *field_var_name = PQgetvalue(result, rowIndex, 1);
01092          char *field_var_val = PQgetvalue(result, rowIndex, 2);
01093          char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01094          if (!strcmp(field_var_name, "#include")) {
01095             if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01096                PQclear(result);
01097                ast_mutex_unlock(&pgsql_lock);
01098                return NULL;
01099             }
01100             continue;
01101          }
01102 
01103          if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01104             cur_cat = ast_category_new(field_category, "", 99999);
01105             if (!cur_cat)
01106                break;
01107             strcpy(last, field_category);
01108             last_cat_metric = atoi(field_cat_metric);
01109             ast_category_append(cfg, cur_cat);
01110          }
01111          new_v = ast_variable_new(field_var_name, field_var_val, "");
01112          ast_variable_append(cur_cat, new_v);
01113       }
01114    } else {
01115       ast_log(LOG_WARNING,
01116             "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01117    }
01118 
01119    PQclear(result);
01120    ast_mutex_unlock(&pgsql_lock);
01121 
01122    return cfg;
01123 }
01124 
01125 static int require_pgsql(const char *database, const char *tablename, va_list ap)
01126 {
01127    struct columns *column;
01128    struct tables *table = find_table(tablename);
01129    char *elm;
01130    int type, size, res = 0;
01131 
01132    if (!table) {
01133       ast_log(LOG_WARNING, "Table %s not found in database.  This table should exist if you're using realtime.\n", tablename);
01134       return -1;
01135    }
01136 
01137    while ((elm = va_arg(ap, char *))) {
01138       type = va_arg(ap, require_type);
01139       size = va_arg(ap, int);
01140       AST_LIST_TRAVERSE(&table->columns, column, list) {
01141          if (strcmp(column->name, elm) == 0) {
01142             /* Char can hold anything, as long as it is large enough */
01143             if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01144                if ((size > column->len) && column->len != -1) {
01145                   ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01146                   res = -1;
01147                }
01148             } else if (strncmp(column->type, "int", 3) == 0) {
01149                int typesize = atoi(column->type + 3);
01150                /* Integers can hold only other integers */
01151                if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01152                   type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01153                   type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01154                   type == RQ_UINTEGER2) && typesize == 2) {
01155                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01156                   res = -1;
01157                } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01158                   type == RQ_UINTEGER4) && typesize == 4) {
01159                   ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01160                   res = -1;
01161                } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01162                   ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01163                      column->name,
01164                         type == RQ_CHAR ? "char" :
01165                         type == RQ_DATETIME ? "datetime" :
01166                         type == RQ_DATE ? "date" :
01167                         type == RQ_FLOAT ? "float" :
01168                         "a rather stiff drink ",
01169                      size, column->type);
01170                   res = -1;
01171                }
01172             } else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) {
01173                ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01174                res = -1;
01175             } else { /* There are other types that no module implements yet */
01176                ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01177                res = -1;
01178             }
01179             break;
01180          }
01181       }
01182 
01183       if (!column) {
01184          if (requirements == RQ_WARN) {
01185             ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01186          } else {
01187             struct ast_str *sql = ast_str_create(100);
01188             char fieldtype[15];
01189             PGresult *result;
01190 
01191             if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01192                /* Size is minimum length; make it at least 50% greater,
01193                 * just to be sure, because PostgreSQL doesn't support
01194                 * resizing columns. */
01195                snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01196                   size < 15 ? size * 2 :
01197                   (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01198             } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01199                snprintf(fieldtype, sizeof(fieldtype), "INT2");
01200             } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01201                snprintf(fieldtype, sizeof(fieldtype), "INT4");
01202             } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01203                snprintf(fieldtype, sizeof(fieldtype), "INT8");
01204             } else if (type == RQ_UINTEGER8) {
01205                /* No such type on PostgreSQL */
01206                snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01207             } else if (type == RQ_FLOAT) {
01208                snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01209             } else if (type == RQ_DATE) {
01210                snprintf(fieldtype, sizeof(fieldtype), "DATE");
01211             } else if (type == RQ_DATETIME) {
01212                snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01213             } else {
01214                ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01215                ast_free(sql);
01216                continue;
01217             }
01218             ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01219             ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01220 
01221             ast_mutex_lock(&pgsql_lock);
01222             if (!pgsql_reconnect(database)) {
01223                ast_mutex_unlock(&pgsql_lock);
01224                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01225                ast_free(sql);
01226                continue;
01227             }
01228 
01229             ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01230             result = PQexec(pgsqlConn, ast_str_buffer(sql));
01231             ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01232             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01233                ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01234             }
01235             PQclear(result);
01236             ast_mutex_unlock(&pgsql_lock);
01237 
01238             ast_free(sql);
01239          }
01240       }
01241    }
01242    release_table(table);
01243    return res;
01244 }
01245 
01246 static int unload_pgsql(const char *database, const char *tablename)
01247 {
01248    struct tables *cur;
01249    ast_debug(2, "About to lock table cache list\n");
01250    AST_LIST_LOCK(&psql_tables);
01251    ast_debug(2, "About to traverse table cache list\n");
01252    AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01253       if (strcmp(cur->name, tablename) == 0) {
01254          ast_debug(2, "About to remove matching cache entry\n");
01255          AST_LIST_REMOVE_CURRENT(list);
01256          ast_debug(2, "About to destroy matching cache entry\n");
01257          destroy_table(cur);
01258          ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01259          break;
01260       }
01261    }
01262    AST_LIST_TRAVERSE_SAFE_END
01263    AST_LIST_UNLOCK(&psql_tables);
01264    ast_debug(2, "About to return\n");
01265    return cur ? 0 : -1;
01266 }
01267 
01268 static struct ast_config_engine pgsql_engine = {
01269    .name = "pgsql",
01270    .load_func = config_pgsql,
01271    .realtime_func = realtime_pgsql,
01272    .realtime_multi_func = realtime_multi_pgsql,
01273    .store_func = store_pgsql,
01274    .destroy_func = destroy_pgsql,
01275    .update_func = update_pgsql,
01276    .update2_func = update2_pgsql,
01277    .require_func = require_pgsql,
01278    .unload_func = unload_pgsql,
01279 };
01280 
01281 static int load_module(void)
01282 {
01283    if(!parse_config(0))
01284       return AST_MODULE_LOAD_DECLINE;
01285 
01286    ast_config_engine_register(&pgsql_engine);
01287    ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01288    ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01289 
01290    return 0;
01291 }
01292 
01293 static int unload_module(void)
01294 {
01295    struct tables *table;
01296    /* Acquire control before doing anything to the module itself. */
01297    ast_mutex_lock(&pgsql_lock);
01298 
01299    if (pgsqlConn) {
01300       PQfinish(pgsqlConn);
01301       pgsqlConn = NULL;
01302    }
01303    ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01304    ast_config_engine_deregister(&pgsql_engine);
01305    ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01306 
01307    /* Destroy cached table info */
01308    AST_LIST_LOCK(&psql_tables);
01309    while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01310       destroy_table(table);
01311    }
01312    AST_LIST_UNLOCK(&psql_tables);
01313 
01314    /* Unlock so something else can destroy the lock. */
01315    ast_mutex_unlock(&pgsql_lock);
01316 
01317    return 0;
01318 }
01319 
01320 static int reload(void)
01321 {
01322    parse_config(1);
01323 
01324    return 0;
01325 }
01326 
01327 static int parse_config(int is_reload)
01328 {
01329    struct ast_config *config;
01330    const char *s;
01331    struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01332 
01333    config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01334    if (config == CONFIG_STATUS_FILEUNCHANGED) {
01335       return 0;
01336    }
01337 
01338    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01339       ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01340       return 0;
01341    }
01342 
01343    ast_mutex_lock(&pgsql_lock);
01344 
01345    if (pgsqlConn) {
01346       PQfinish(pgsqlConn);
01347       pgsqlConn = NULL;
01348    }
01349 
01350    if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01351       ast_log(LOG_WARNING,
01352             "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01353       strcpy(dbuser, "asterisk");
01354    } else {
01355       ast_copy_string(dbuser, s, sizeof(dbuser));
01356    }
01357 
01358    if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01359       ast_log(LOG_WARNING,
01360             "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01361       strcpy(dbpass, "asterisk");
01362    } else {
01363       ast_copy_string(dbpass, s, sizeof(dbpass));
01364    }
01365 
01366    if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01367       ast_log(LOG_WARNING,
01368             "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01369       dbhost[0] = '\0';
01370    } else {
01371       ast_copy_string(dbhost, s, sizeof(dbhost));
01372    }
01373 
01374    if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01375       ast_log(LOG_WARNING,
01376             "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01377       strcpy(dbname, "asterisk");
01378    } else {
01379       ast_copy_string(dbname, s, sizeof(dbname));
01380    }
01381 
01382    if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01383       ast_log(LOG_WARNING,
01384             "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01385       dbport = 5432;
01386    } else {
01387       dbport = atoi(s);
01388    }
01389 
01390    if (!ast_strlen_zero(dbhost)) {
01391       /* No socket needed */
01392    } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01393       ast_log(LOG_WARNING,
01394             "PostgreSQL RealTime: No database socket found, using '/tmp/pgsql.sock' as default.\n");
01395       strcpy(dbsock, "/tmp/pgsql.sock");
01396    } else {
01397       ast_copy_string(dbsock, s, sizeof(dbsock));
01398    }
01399 
01400    if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01401       ast_log(LOG_WARNING,
01402             "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01403       requirements = RQ_WARN;
01404    } else if (!strcasecmp(s, "createclose")) {
01405       requirements = RQ_CREATECLOSE;
01406    } else if (!strcasecmp(s, "createchar")) {
01407       requirements = RQ_CREATECHAR;
01408    }
01409 
01410    ast_config_destroy(config);
01411 
01412    if (option_debug) {
01413       if (!ast_strlen_zero(dbhost)) {
01414          ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01415          ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01416       } else {
01417          ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01418       }
01419       ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01420       ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01421       ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01422    }
01423 
01424    if (!pgsql_reconnect(NULL)) {
01425       ast_log(LOG_WARNING,
01426             "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01427       ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01428    }
01429 
01430    ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01431 
01432    /* Done reloading. Release lock so others can now use driver. */
01433    ast_mutex_unlock(&pgsql_lock);
01434 
01435    return 1;
01436 }
01437 
01438 static int pgsql_reconnect(const char *database)
01439 {
01440    char my_database[50];
01441 
01442    ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01443 
01444    /* mutex lock should have been locked before calling this function. */
01445 
01446    if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01447       PQfinish(pgsqlConn);
01448       pgsqlConn = NULL;
01449    }
01450 
01451    /* DB password can legitimately be 0-length */
01452    if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01453       struct ast_str *connInfo = ast_str_create(32);
01454 
01455       ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01456          dbhost, dbport, my_database, dbuser);
01457       if (!ast_strlen_zero(dbpass))
01458          ast_str_append(&connInfo, 0, " password=%s", dbpass);
01459 
01460       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01461       pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01462       ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01463       ast_free(connInfo);
01464       connInfo = NULL;
01465 
01466       ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01467       if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01468          ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01469          connect_time = time(NULL);
01470          version = PQserverVersion(pgsqlConn);
01471          return 1;
01472       } else {
01473          ast_log(LOG_ERROR,
01474                "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01475                dbname, dbhost, PQresultErrorMessage(NULL));
01476          return 0;
01477       }
01478    } else {
01479       ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01480       return 1;
01481    }
01482 }
01483 
01484 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01485 {
01486    struct tables *cur;
01487    int l, which;
01488    char *ret = NULL;
01489 
01490    switch (cmd) {
01491    case CLI_INIT:
01492       e->command = "realtime show pgsql cache";
01493       e->usage =
01494          "Usage: realtime show pgsql cache [<table>]\n"
01495          "       Shows table cache for the PostgreSQL RealTime driver\n";
01496       return NULL;
01497    case CLI_GENERATE:
01498       if (a->argc != 4) {
01499          return NULL;
01500       }
01501       l = strlen(a->word);
01502       which = 0;
01503       AST_LIST_LOCK(&psql_tables);
01504       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01505          if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01506             ret = ast_strdup(cur->name);
01507             break;
01508          }
01509       }
01510       AST_LIST_UNLOCK(&psql_tables);
01511       return ret;
01512    }
01513 
01514    if (a->argc == 4) {
01515       /* List of tables */
01516       AST_LIST_LOCK(&psql_tables);
01517       AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01518          ast_cli(a->fd, "%s\n", cur->name);
01519       }
01520       AST_LIST_UNLOCK(&psql_tables);
01521    } else if (a->argc == 5) {
01522       /* List of columns */
01523       if ((cur = find_table(a->argv[4]))) {
01524          struct columns *col;
01525          ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01526          ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01527          AST_LIST_TRAVERSE(&cur->columns, col, list) {
01528             ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01529          }
01530          release_table(cur);
01531       } else {
01532          ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01533       }
01534    }
01535    return 0;
01536 }
01537 
01538 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01539 {
01540    char status[256], credentials[100] = "";
01541    int ctimesec = time(NULL) - connect_time;
01542 
01543    switch (cmd) {
01544    case CLI_INIT:
01545       e->command = "realtime show pgsql status";
01546       e->usage =
01547          "Usage: realtime show pgsql status\n"
01548          "       Shows connection information for the PostgreSQL RealTime driver\n";
01549       return NULL;
01550    case CLI_GENERATE:
01551       return NULL;
01552    }
01553 
01554    if (a->argc != 4)
01555       return CLI_SHOWUSAGE;
01556 
01557    if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01558       if (!ast_strlen_zero(dbhost))
01559          snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01560       else if (!ast_strlen_zero(dbsock))
01561          snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01562       else
01563          snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01564 
01565       if (!ast_strlen_zero(dbuser))
01566          snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01567 
01568       if (ctimesec > 31536000)
01569          ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01570                status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01571                (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01572       else if (ctimesec > 86400)
01573          ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01574                credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01575                ctimesec % 60);
01576       else if (ctimesec > 3600)
01577          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01578                ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01579       else if (ctimesec > 60)
01580          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01581                ctimesec % 60);
01582       else
01583          ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01584 
01585       return CLI_SUCCESS;
01586    } else {
01587       return CLI_FAILURE;
01588    }
01589 }
01590 
01591 /* needs usecount semantics defined */
01592 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PostgreSQL RealTime Configuration Driver",
01593       .load = load_module,
01594       .unload = unload_module,
01595       .reload = reload
01596           );