Mon Apr 30 07:36:36 2007

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

Generated on Mon Apr 30 07:36:36 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1