Mon Mar 31 07:38:04 2008

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

Generated on Mon Mar 31 07:38:04 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1