Sat Aug 6 00:39:31 2011

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-2010, 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: 283880 $")
00032 
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include <string.h>
00036 #include <libpq-fe.h>         /* PostgreSQL */
00037 
00038 #include "asterisk/file.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/options.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/cli.h"
00048 
00049 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00050 
00051 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
00052 
00053 PGconn *pgsqlConn = NULL;
00054 
00055 #define MAX_DB_OPTION_SIZE 64
00056 
00057 static char dbhost[MAX_DB_OPTION_SIZE] = "";
00058 static char dbuser[MAX_DB_OPTION_SIZE] = "";
00059 static char dbpass[MAX_DB_OPTION_SIZE] = "";
00060 static char dbname[MAX_DB_OPTION_SIZE] = "";
00061 static char dbsock[MAX_DB_OPTION_SIZE] = "";
00062 static int dbport = 5432;
00063 static time_t connect_time = 0;
00064 
00065 static int parse_config(void);
00066 static int pgsql_reconnect(const char *database);
00067 static int realtime_pgsql_status(int fd, int argc, char **argv);
00068 
00069 static char cli_realtime_pgsql_status_usage[] =
00070    "Usage: realtime pgsql status\n"
00071    "       Shows connection information for the Postgresql RealTime driver\n";
00072 
00073 static struct ast_cli_entry cli_realtime[] = {
00074    { { "realtime", "pgsql", "status", NULL },
00075    realtime_pgsql_status, "Shows connection information for the Postgresql RealTime driver",
00076    cli_realtime_pgsql_status_usage },
00077 };
00078 
00079 static char *encode_chunk(const char *chunk, char *buf, size_t len)
00080 {
00081    char *cptr = buf;
00082    for (; *chunk && cptr < buf + len; chunk++) {
00083       if (strchr(";^", *chunk)) {
00084          snprintf(cptr, buf + len - cptr, "^%02hhX", *chunk);
00085          cptr += 3;
00086       } else {
00087          *cptr++ = *chunk;
00088       }
00089    }
00090    if (cptr < buf + len) {
00091       *cptr = '\0';
00092    } else {
00093       buf[len - 1] = '\0';
00094    }
00095    return buf;
00096 }
00097 
00098 static char *decode_chunk(char *chunk)
00099 {
00100    char *orig = chunk;
00101    for (; *chunk; chunk++) {
00102       if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
00103          sscanf(chunk + 1, "%02hhX", chunk);
00104          memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
00105       }
00106    }
00107    return orig;
00108 }
00109 
00110 static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap)
00111 {
00112    PGresult *result = NULL;
00113    int num_rows = 0, pgerror;
00114    char sql[256], escapebuf[2049], semibuf[1024];
00115    char *stringp;
00116    char *chunk;
00117    char *op;
00118    const char *newparam, *newval;
00119    struct ast_variable *var = NULL, *prev = NULL;
00120 
00121    if (!table) {
00122       ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
00123       return NULL;
00124    }
00125 
00126    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00127    newparam = va_arg(ap, const char *);
00128    newval = va_arg(ap, const char *);
00129    if (!newparam || !newval) {
00130       ast_log(LOG_WARNING,
00131             "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00132       if (pgsqlConn) {
00133          PQfinish(pgsqlConn);
00134          pgsqlConn = NULL;
00135       };
00136       return NULL;
00137    }
00138 
00139    /* Create the first part of the query using the first parameter/value pairs we just extracted
00140       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00141    op = strchr(newparam, ' ') ? "" : " =";
00142 
00143    PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00144    if (pgerror) {
00145       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00146       va_end(ap);
00147       return NULL;
00148    }
00149 
00150    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
00151           escapebuf);
00152    while ((newparam = va_arg(ap, const char *))) {
00153       newval = va_arg(ap, const char *);
00154       if (!strchr(newparam, ' '))
00155          op = " =";
00156       else
00157          op = "";
00158 
00159       PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00160       if (pgerror) {
00161          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00162          va_end(ap);
00163          return NULL;
00164       }
00165 
00166       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
00167              op, escapebuf);
00168    }
00169    va_end(ap);
00170 
00171    /* We now have our complete statement; Lets connect to the server and execute it. */
00172    ast_mutex_lock(&pgsql_lock);
00173    if (!pgsql_reconnect(database)) {
00174       ast_mutex_unlock(&pgsql_lock);
00175       return NULL;
00176    }
00177 
00178    if (!(result = PQexec(pgsqlConn, sql))) {
00179       ast_log(LOG_WARNING,
00180             "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00181       ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00182       ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00183             PQerrorMessage(pgsqlConn));
00184       ast_mutex_unlock(&pgsql_lock);
00185       return NULL;
00186    } else {
00187       ExecStatusType result_status = PQresultStatus(result);
00188       if (result_status != PGRES_COMMAND_OK
00189          && result_status != PGRES_TUPLES_OK
00190          && result_status != PGRES_NONFATAL_ERROR) {
00191          ast_log(LOG_WARNING,
00192                "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00193          ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00194          ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00195                PQresultErrorMessage(result), PQresStatus(result_status));
00196          ast_mutex_unlock(&pgsql_lock);
00197          return NULL;
00198       }
00199    }
00200 
00201    ast_log(LOG_DEBUG, "Postgresql RealTime: Result=%p Query: %s\n", result, sql);
00202 
00203    if ((num_rows = PQntuples(result)) > 0) {
00204       int i = 0;
00205       int rowIndex = 0;
00206       int numFields = PQnfields(result);
00207       char **fieldnames = NULL;
00208 
00209       ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows);
00210 
00211       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00212          ast_mutex_unlock(&pgsql_lock);
00213          PQclear(result);
00214          return NULL;
00215       }
00216       for (i = 0; i < numFields; i++)
00217          fieldnames[i] = PQfname(result, i);
00218       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00219          for (i = 0; i < numFields; i++) {
00220             stringp = PQgetvalue(result, rowIndex, i);
00221             while (stringp) {
00222                chunk = strsep(&stringp, ";");
00223                if (chunk && !ast_strlen_zero(decode_chunk(ast_strip(chunk)))) {
00224                   if (prev) {
00225                      prev->next = ast_variable_new(fieldnames[i], chunk);
00226                      if (prev->next) {
00227                         prev = prev->next;
00228                      }
00229                   } else {
00230                      prev = var = ast_variable_new(fieldnames[i], chunk);
00231                   }
00232                }
00233             }
00234          }
00235       }
00236       ast_free(fieldnames);
00237    } else {
00238       ast_log(LOG_DEBUG, "Postgresql RealTime: Could not find any rows in table %s.\n", table);
00239    }
00240 
00241    ast_mutex_unlock(&pgsql_lock);
00242    PQclear(result);
00243 
00244    return var;
00245 }
00246 
00247 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
00248 {
00249    PGresult *result = NULL;
00250    int num_rows = 0, pgerror;
00251    char sql[256], escapebuf[2049], semibuf[1024];
00252    const char *initfield = NULL;
00253    char *stringp;
00254    char *chunk;
00255    char *op;
00256    const char *newparam, *newval;
00257    struct ast_realloca ra;
00258    struct ast_variable *var = NULL;
00259    struct ast_config *cfg = NULL;
00260    struct ast_category *cat = NULL;
00261 
00262    if (!table) {
00263       ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
00264       return NULL;
00265    }
00266 
00267    memset(&ra, 0, sizeof(ra));
00268 
00269    if (!(cfg = ast_config_new()))
00270       return NULL;
00271 
00272    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00273    newparam = va_arg(ap, const char *);
00274    newval = va_arg(ap, const char *);
00275    if (!newparam || !newval) {
00276       ast_log(LOG_WARNING,
00277             "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00278       if (pgsqlConn) {
00279          PQfinish(pgsqlConn);
00280          pgsqlConn = NULL;
00281       };
00282       return NULL;
00283    }
00284 
00285    initfield = ast_strdupa(newparam);
00286    if ((op = strchr(initfield, ' '))) {
00287       *op = '\0';
00288    }
00289 
00290    /* Create the first part of the query using the first parameter/value pairs we just extracted
00291       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00292 
00293    if (!strchr(newparam, ' '))
00294       op = " =";
00295    else
00296       op = "";
00297 
00298    PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00299    if (pgerror) {
00300       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00301       va_end(ap);
00302       return NULL;
00303    }
00304 
00305    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
00306           escapebuf);
00307    while ((newparam = va_arg(ap, const char *))) {
00308       newval = va_arg(ap, const char *);
00309       if (!strchr(newparam, ' '))
00310          op = " =";
00311       else
00312          op = "";
00313 
00314       PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00315       if (pgerror) {
00316          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00317          va_end(ap);
00318          return NULL;
00319       }
00320 
00321       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
00322              op, escapebuf);
00323    }
00324 
00325    if (initfield) {
00326       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00327    }
00328 
00329    va_end(ap);
00330 
00331    /* We now have our complete statement; Lets connect to the server and execute it. */
00332    ast_mutex_lock(&pgsql_lock);
00333    if (!pgsql_reconnect(database)) {
00334       ast_mutex_unlock(&pgsql_lock);
00335       return NULL;
00336    }
00337 
00338    if (!(result = PQexec(pgsqlConn, sql))) {
00339       ast_log(LOG_WARNING,
00340             "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00341       ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00342       ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00343             PQerrorMessage(pgsqlConn));
00344       ast_mutex_unlock(&pgsql_lock);
00345       return NULL;
00346    } else {
00347       ExecStatusType result_status = PQresultStatus(result);
00348       if (result_status != PGRES_COMMAND_OK
00349          && result_status != PGRES_TUPLES_OK
00350          && result_status != PGRES_NONFATAL_ERROR) {
00351          ast_log(LOG_WARNING,
00352                "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00353          ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00354          ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00355                PQresultErrorMessage(result), PQresStatus(result_status));
00356          ast_mutex_unlock(&pgsql_lock);
00357          return NULL;
00358       }
00359    }
00360 
00361    ast_log(LOG_DEBUG, "2Postgresql RealTime: Result=%p Query: %s\n", result, sql);
00362 
00363    if ((num_rows = PQntuples(result)) > 0) {
00364       int numFields = PQnfields(result);
00365       int i = 0;
00366       int rowIndex = 0;
00367       char **fieldnames = NULL;
00368 
00369       ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows);
00370 
00371       if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00372          ast_mutex_unlock(&pgsql_lock);
00373          PQclear(result);
00374          return NULL;
00375       }
00376       for (i = 0; i < numFields; i++)
00377          fieldnames[i] = PQfname(result, i);
00378 
00379       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00380          var = NULL;
00381          if (!(cat = ast_category_new("")))
00382             continue;
00383          for (i = 0; i < numFields; i++) {
00384             stringp = PQgetvalue(result, rowIndex, i);
00385             while (stringp) {
00386                chunk = strsep(&stringp, ";");
00387                if (chunk && !ast_strlen_zero(decode_chunk(ast_strip(chunk)))) {
00388                   if (initfield && !strcmp(initfield, fieldnames[i])) {
00389                      ast_category_rename(cat, chunk);
00390                   }
00391                   var = ast_variable_new(fieldnames[i], chunk);
00392                   ast_variable_append(cat, var);
00393                }
00394             }
00395          }
00396          ast_category_append(cfg, cat);
00397       }
00398       ast_free(fieldnames);
00399    } else {
00400       ast_log(LOG_DEBUG,
00401             "Postgresql RealTime: Could not find any rows in table %s.\n", table);
00402    }
00403 
00404    ast_mutex_unlock(&pgsql_lock);
00405    PQclear(result);
00406 
00407    return cfg;
00408 }
00409 
00410 static int update_pgsql(const char *database, const char *table, const char *keyfield,
00411                   const char *lookup, va_list ap)
00412 {
00413    PGresult *result = NULL;
00414    int numrows = 0, pgerror;
00415    char sql[256], escapebuf[2049], semibuf[1024];
00416    const char *newparam, *newval;
00417 
00418    if (!table) {
00419       ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
00420       return -1;
00421    }
00422 
00423    /* Get the first parameter and first value in our list of passed paramater/value pairs */
00424    newparam = va_arg(ap, const char *);
00425    newval = va_arg(ap, const char *);
00426    if (!newparam || !newval) {
00427       ast_log(LOG_WARNING,
00428             "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00429       if (pgsqlConn) {
00430          PQfinish(pgsqlConn);
00431          pgsqlConn = NULL;
00432       };
00433       return -1;
00434    }
00435 
00436    /* Create the first part of the query using the first parameter/value pairs we just extracted
00437       If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
00438 
00439    PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00440    if (pgerror) {
00441       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00442       va_end(ap);
00443       return -1;
00444    }
00445    snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, escapebuf);
00446 
00447    while ((newparam = va_arg(ap, const char *))) {
00448       newval = va_arg(ap, const char *);
00449 
00450       PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00451       if (pgerror) {
00452          ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00453          va_end(ap);
00454          return -1;
00455       }
00456 
00457       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam,
00458              escapebuf);
00459    }
00460    va_end(ap);
00461 
00462    PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror);
00463    if (pgerror) {
00464       ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
00465       va_end(ap);
00466       return -1;
00467    }
00468 
00469    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield,
00470           escapebuf);
00471 
00472    ast_log(LOG_DEBUG, "Postgresql RealTime: Update SQL: %s\n", sql);
00473 
00474    /* We now have our complete statement; Lets connect to the server and execute it. */
00475    ast_mutex_lock(&pgsql_lock);
00476    if (!pgsql_reconnect(database)) {
00477       ast_mutex_unlock(&pgsql_lock);
00478       return -1;
00479    }
00480 
00481    if (!(result = PQexec(pgsqlConn, sql))) {
00482       ast_log(LOG_WARNING,
00483             "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00484       ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00485       ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00486             PQerrorMessage(pgsqlConn));
00487       ast_mutex_unlock(&pgsql_lock);
00488       return -1;
00489    } else {
00490       ExecStatusType result_status = PQresultStatus(result);
00491       if (result_status != PGRES_COMMAND_OK
00492          && result_status != PGRES_TUPLES_OK
00493          && result_status != PGRES_NONFATAL_ERROR) {
00494          ast_log(LOG_WARNING,
00495                "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00496          ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00497          ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00498                PQresultErrorMessage(result), PQresStatus(result_status));
00499          ast_mutex_unlock(&pgsql_lock);
00500          return -1;
00501       }
00502    }
00503 
00504    numrows = atoi(PQcmdTuples(result));
00505    ast_mutex_unlock(&pgsql_lock);
00506 
00507    ast_log(LOG_DEBUG, "Postgresql RealTime: Updated %d rows on table: %s\n", numrows,
00508          table);
00509 
00510    /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
00511     * An integer greater than zero indicates the number of rows affected
00512     * Zero indicates that no records were updated
00513     * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
00514     */
00515 
00516    if (numrows >= 0)
00517       return (int) numrows;
00518 
00519    return -1;
00520 }
00521 
00522 static struct ast_config *config_pgsql(const char *database, const char *table,
00523                   const char *file, struct ast_config *cfg,
00524                   int withcomments)
00525 {
00526    PGresult *result = NULL;
00527    long num_rows;
00528    struct ast_variable *new_v;
00529    struct ast_category *cur_cat = NULL;
00530    char sqlbuf[1024] = "";
00531    char *sql = sqlbuf;
00532    size_t sqlleft = sizeof(sqlbuf);
00533    char last[80] = "";
00534    int last_cat_metric = 0;
00535 
00536    last[0] = '\0';
00537 
00538    if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
00539       ast_log(LOG_WARNING, "Postgresql RealTime: Cannot configure myself.\n");
00540       return NULL;
00541    }
00542 
00543    ast_build_string(&sql, &sqlleft, "SELECT category, var_name, var_val, cat_metric FROM %s ", table);
00544    ast_build_string(&sql, &sqlleft, "WHERE filename='%s' and commented=0", file);
00545    ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
00546 
00547    ast_log(LOG_DEBUG, "Postgresql RealTime: Static SQL: %s\n", sqlbuf);
00548 
00549    /* We now have our complete statement; Lets connect to the server and execute it. */
00550    ast_mutex_lock(&pgsql_lock);
00551    if (!pgsql_reconnect(database)) {
00552       ast_mutex_unlock(&pgsql_lock);
00553       return NULL;
00554    }
00555 
00556    if (!(result = PQexec(pgsqlConn, sqlbuf))) {
00557       ast_log(LOG_WARNING,
00558             "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00559       ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00560       ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00561             PQerrorMessage(pgsqlConn));
00562       ast_mutex_unlock(&pgsql_lock);
00563       return NULL;
00564    } else {
00565       ExecStatusType result_status = PQresultStatus(result);
00566       if (result_status != PGRES_COMMAND_OK
00567          && result_status != PGRES_TUPLES_OK
00568          && result_status != PGRES_NONFATAL_ERROR) {
00569          ast_log(LOG_WARNING,
00570                "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00571          ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00572          ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00573                PQresultErrorMessage(result), PQresStatus(result_status));
00574          ast_mutex_unlock(&pgsql_lock);
00575          return NULL;
00576       }
00577    }
00578 
00579    if ((num_rows = PQntuples(result)) > 0) {
00580       int rowIndex = 0;
00581 
00582       ast_log(LOG_DEBUG, "Postgresql RealTime: Found %ld rows.\n", num_rows);
00583 
00584       for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00585          char *field_category = PQgetvalue(result, rowIndex, 0);
00586          char *field_var_name = PQgetvalue(result, rowIndex, 1);
00587          char *field_var_val = PQgetvalue(result, rowIndex, 2);
00588          char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
00589          if (!strcmp(field_var_name, "#include")) {
00590             if (!ast_config_internal_load(field_var_val, cfg, 0)) {
00591                PQclear(result);
00592                ast_mutex_unlock(&pgsql_lock);
00593                return NULL;
00594             }
00595             continue;
00596          }
00597 
00598          if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
00599             cur_cat = ast_category_new(field_category);
00600             if (!cur_cat)
00601                break;
00602             strcpy(last, field_category);
00603             last_cat_metric = atoi(field_cat_metric);
00604             ast_category_append(cfg, cur_cat);
00605          }
00606          new_v = ast_variable_new(field_var_name, field_var_val);
00607          ast_variable_append(cur_cat, new_v);
00608       }
00609    } else {
00610       ast_log(LOG_WARNING,
00611             "Postgresql RealTime: Could not find config '%s' in database.\n", file);
00612    }
00613 
00614    PQclear(result);
00615    ast_mutex_unlock(&pgsql_lock);
00616 
00617    return cfg;
00618 }
00619 
00620 static struct ast_config_engine pgsql_engine = {
00621    .name = "pgsql",
00622    .load_func = config_pgsql,
00623    .realtime_func = realtime_pgsql,
00624    .realtime_multi_func = realtime_multi_pgsql,
00625    .update_func = update_pgsql
00626 };
00627 
00628 static int load_module(void)
00629 {
00630    if(!parse_config())
00631       return AST_MODULE_LOAD_DECLINE;
00632 
00633    ast_mutex_lock(&pgsql_lock);
00634 
00635    if (!pgsql_reconnect(NULL)) {
00636       ast_log(LOG_WARNING,
00637             "Postgresql RealTime: Couldn't establish connection. Check debug.\n");
00638       ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n",
00639             PQerrorMessage(pgsqlConn));
00640    }
00641 
00642    ast_config_engine_register(&pgsql_engine);
00643    if (option_verbose) {
00644       ast_verbose("Postgresql RealTime driver loaded.\n");
00645    }
00646    ast_cli_register_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
00647 
00648    ast_mutex_unlock(&pgsql_lock);
00649 
00650    return 0;
00651 }
00652 
00653 static int unload_module(void)
00654 {
00655    /* Aquire control before doing anything to the module itself. */
00656    ast_mutex_lock(&pgsql_lock);
00657 
00658    if (pgsqlConn) {
00659       PQfinish(pgsqlConn);
00660       pgsqlConn = NULL;
00661    };
00662    ast_cli_unregister_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
00663    ast_config_engine_deregister(&pgsql_engine);
00664    if (option_verbose) {
00665       ast_verbose("Postgresql RealTime unloaded.\n");
00666    }
00667 
00668    ast_module_user_hangup_all();
00669 
00670    /* Unlock so something else can destroy the lock. */
00671    ast_mutex_unlock(&pgsql_lock);
00672 
00673    return 0;
00674 }
00675 
00676 static int reload(void)
00677 {
00678    /* Aquire control before doing anything to the module itself. */
00679    ast_mutex_lock(&pgsql_lock);
00680 
00681    if (pgsqlConn) {
00682       PQfinish(pgsqlConn);
00683       pgsqlConn = NULL;
00684    };
00685    parse_config();
00686 
00687    if (!pgsql_reconnect(NULL)) {
00688       ast_log(LOG_WARNING,
00689             "Postgresql RealTime: Couldn't establish connection. Check debug.\n");
00690       ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n",
00691             PQerrorMessage(pgsqlConn));
00692    }
00693 
00694    ast_verbose(VERBOSE_PREFIX_2 "Postgresql RealTime reloaded.\n");
00695 
00696    /* Done reloading. Release lock so others can now use driver. */
00697    ast_mutex_unlock(&pgsql_lock);
00698 
00699    return 0;
00700 }
00701 
00702 static int parse_config(void)
00703 {
00704    struct ast_config *config;
00705    const char *s;
00706 
00707    config = ast_config_load(RES_CONFIG_PGSQL_CONF);
00708 
00709    if (!config) {
00710       ast_log(LOG_WARNING, "Unable to load config %s\n",RES_CONFIG_PGSQL_CONF);
00711       return 0;
00712    }
00713    if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
00714       ast_log(LOG_WARNING,
00715             "Postgresql RealTime: No database user found, using 'asterisk' as default.\n");
00716       strcpy(dbuser, "asterisk");
00717    } else {
00718       ast_copy_string(dbuser, s, sizeof(dbuser));
00719    }
00720 
00721    if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
00722       ast_log(LOG_WARNING,
00723             "Postgresql RealTime: No database password found, using 'asterisk' as default.\n");
00724       strcpy(dbpass, "asterisk");
00725    } else {
00726       ast_copy_string(dbpass, s, sizeof(dbpass));
00727    }
00728 
00729    if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
00730       ast_log(LOG_WARNING,
00731             "Postgresql RealTime: No database host found, using localhost via socket.\n");
00732       dbhost[0] = '\0';
00733    } else {
00734       ast_copy_string(dbhost, s, sizeof(dbhost));
00735    }
00736 
00737    if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
00738       ast_log(LOG_WARNING,
00739             "Postgresql RealTime: No database name found, using 'asterisk' as default.\n");
00740       strcpy(dbname, "asterisk");
00741    } else {
00742       ast_copy_string(dbname, s, sizeof(dbname));
00743    }
00744 
00745    if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
00746       ast_log(LOG_WARNING,
00747             "Postgresql RealTime: No database port found, using 5432 as default.\n");
00748       dbport = 5432;
00749    } else {
00750       dbport = atoi(s);
00751    }
00752 
00753    if (!ast_strlen_zero(dbhost)) {
00754       /* No socket needed */
00755    } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
00756       ast_log(LOG_WARNING,
00757             "Postgresql RealTime: No database socket found, using '/tmp/pgsql.sock' as default.\n");
00758       strcpy(dbsock, "/tmp/pgsql.sock");
00759    } else {
00760       ast_copy_string(dbsock, s, sizeof(dbsock));
00761    }
00762    ast_config_destroy(config);
00763 
00764    if (!ast_strlen_zero(dbhost)) {
00765       ast_log(LOG_DEBUG, "Postgresql RealTime Host: %s\n", dbhost);
00766       ast_log(LOG_DEBUG, "Postgresql RealTime Port: %i\n", dbport);
00767    } else {
00768       ast_log(LOG_DEBUG, "Postgresql RealTime Socket: %s\n", dbsock);
00769    }
00770    ast_log(LOG_DEBUG, "Postgresql RealTime User: %s\n", dbuser);
00771    ast_log(LOG_DEBUG, "Postgresql RealTime Password: %s\n", dbpass);
00772    ast_log(LOG_DEBUG, "Postgresql RealTime DBName: %s\n", dbname);
00773 
00774    return 1;
00775 }
00776 
00777 static int pgsql_reconnect(const char *database)
00778 {
00779    char my_database[50];
00780 
00781    ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
00782 
00783    /* mutex lock should have been locked before calling this function. */
00784 
00785    if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
00786       PQfinish(pgsqlConn);
00787       pgsqlConn = NULL;
00788    }
00789 
00790    if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(dbpass) && !ast_strlen_zero(my_database)) {
00791       char *connInfo = NULL;
00792       unsigned int size = 100 + strlen(dbhost)
00793          + strlen(dbuser)
00794          + strlen(dbpass)
00795          + strlen(my_database);
00796       
00797       if (!(connInfo = ast_malloc(size)))
00798          return 0;
00799       
00800       sprintf(connInfo, "host=%s port=%d dbname=%s user=%s password=%s",
00801                dbhost, dbport, my_database, dbuser, dbpass);
00802       ast_log(LOG_DEBUG, "%u connInfo=%s\n", size, connInfo);
00803       pgsqlConn = PQconnectdb(connInfo);
00804       ast_log(LOG_DEBUG, "%u connInfo=%s\n", size, connInfo);
00805       ast_free(connInfo);
00806       connInfo = NULL;
00807       ast_log(LOG_DEBUG, "pgsqlConn=%p\n", pgsqlConn);
00808       if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
00809          ast_log(LOG_DEBUG, "Postgresql RealTime: Successfully connected to database.\n");
00810          connect_time = time(NULL);
00811          return 1;
00812       } else {
00813          ast_log(LOG_ERROR,
00814                "Postgresql RealTime: Failed to connect database server %s on %s. Check debug for more info.\n",
00815                dbname, dbhost);
00816          ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n",
00817                PQresultErrorMessage(NULL));
00818          return 0;
00819       }
00820    } else {
00821       ast_log(LOG_DEBUG, "Postgresql RealTime: Everything is fine.\n");
00822       return 1;
00823    }
00824 }
00825 
00826 static int realtime_pgsql_status(int fd, int argc, char **argv)
00827 {
00828    char status[256], status2[100] = "";
00829    int ctime = time(NULL) - connect_time;
00830 
00831    if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
00832       if (!ast_strlen_zero(dbhost)) {
00833          snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
00834       } else if (!ast_strlen_zero(dbsock)) {
00835          snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
00836       } else {
00837          snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
00838       }
00839 
00840       if (!ast_strlen_zero(dbuser)) {
00841          snprintf(status2, 99, " with username %s", dbuser);
00842       }
00843 
00844       if (ctime > 31536000) {
00845          ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
00846                status, status2, ctime / 31536000, (ctime % 31536000) / 86400,
00847                (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00848       } else if (ctime > 86400) {
00849          ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
00850                status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60,
00851                ctime % 60);
00852       } else if (ctime > 3600) {
00853          ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2,
00854                ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00855       } else if (ctime > 60) {
00856          ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60,
00857                ctime % 60);
00858       } else {
00859          ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
00860       }
00861 
00862       return RESULT_SUCCESS;
00863    } else {
00864       return RESULT_FAILURE;
00865    }
00866 }
00867 
00868 /* needs usecount semantics defined */
00869 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL RealTime Configuration Driver",
00870       .load = load_module,
00871       .unload = unload_module,
00872       .reload = reload
00873           );

Generated on Sat Aug 6 00:39:31 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7