Mon Jun 27 16:51:04 2011

Asterisk developer's documentation


cdr_pgsql.c File Reference

PostgreSQL CDR logger. More...

#include "asterisk.h"
#include <libpq-fe.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"

Go to the source code of this file.

Data Structures

struct  columns
struct  psql_columns

Defines

#define DATE_FORMAT   "'%Y-%m-%d %T'"
#define LENGTHEN_BUF1(size)
#define LENGTHEN_BUF2(size)

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int config_module (int reload)
static int load_module (void)
static int pgsql_log (struct ast_cdr *cdr)
static int reload (void)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static const char config [] = "cdr_pgsql.conf"
static PGconn * conn = NULL
static int connected = 0
static char * encoding = NULL
static int maxsize = 512
static int maxsize2 = 512
static const char name [] = "pgsql"
static char * pgdbname = NULL
static char * pgdbport = NULL
static char * pgdbuser = NULL
static char * pghostname = NULL
static char * pgpassword = NULL
static ast_mutex_t pgsql_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static char * table = NULL
static char * tz = NULL


Detailed Description

PostgreSQL CDR logger.

Author:
Matthew D. Hardeman <mhardemn@papersoft.com>
ExtRef:
PostgreSQL http://www.postgresql.org/
See also

Definition in file cdr_pgsql.c.


Define Documentation

#define DATE_FORMAT   "'%Y-%m-%d %T'"

Definition at line 51 of file cdr_pgsql.c.

#define LENGTHEN_BUF1 ( size   ) 

Definition at line 74 of file cdr_pgsql.c.

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 88 of file cdr_pgsql.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 649 of file cdr_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 649 of file cdr_pgsql.c.

static int config_module ( int  reload  )  [static]

Definition at line 378 of file cdr_pgsql.c.

References ast_calloc, ast_cdr_register(), ast_config_destroy(), ast_config_load, ast_debug, ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, AST_RWLIST_INSERT_TAIL, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, columns::list, LOG_ERROR, LOG_WARNING, option_debug, pgsql_log(), unload_module, var, and version.

Referenced by load_module(), and reload().

00379 {
00380    struct ast_variable *var;
00381    char *pgerror;
00382    struct columns *cur;
00383    PGresult *result;
00384    const char *tmp;
00385    struct ast_config *cfg;
00386    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00387 
00388    if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00389       ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00390       return -1;
00391    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00392       return 0;
00393 
00394    if (!(var = ast_variable_browse(cfg, "global"))) {
00395       ast_config_destroy(cfg);
00396       return 0;
00397    }
00398 
00399    if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00400       ast_log(LOG_WARNING, "PostgreSQL server hostname not specified.  Assuming unix socket connection\n");
00401       tmp = "";   /* connect via UNIX-socket by default */
00402    }
00403 
00404    if (pghostname)
00405       ast_free(pghostname);
00406    if (!(pghostname = ast_strdup(tmp))) {
00407       ast_config_destroy(cfg);
00408       return -1;
00409    }
00410 
00411    if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00412       ast_log(LOG_WARNING, "PostgreSQL database not specified.  Assuming asterisk\n");
00413       tmp = "asteriskcdrdb";
00414    }
00415 
00416    if (pgdbname)
00417       ast_free(pgdbname);
00418    if (!(pgdbname = ast_strdup(tmp))) {
00419       ast_config_destroy(cfg);
00420       return -1;
00421    }
00422 
00423    if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00424       ast_log(LOG_WARNING, "PostgreSQL database user not specified.  Assuming asterisk\n");
00425       tmp = "asterisk";
00426    }
00427 
00428    if (pgdbuser)
00429       ast_free(pgdbuser);
00430    if (!(pgdbuser = ast_strdup(tmp))) {
00431       ast_config_destroy(cfg);
00432       return -1;
00433    }
00434 
00435    if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00436       ast_log(LOG_WARNING, "PostgreSQL database password not specified.  Assuming blank\n");
00437       tmp = "";
00438    }
00439 
00440    if (pgpassword)
00441       ast_free(pgpassword);
00442    if (!(pgpassword = ast_strdup(tmp))) {
00443       ast_config_destroy(cfg);
00444       return -1;
00445    }
00446 
00447    if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00448       ast_log(LOG_WARNING, "PostgreSQL database port not specified.  Using default 5432.\n");
00449       tmp = "5432";
00450    }
00451 
00452    if (pgdbport)
00453       ast_free(pgdbport);
00454    if (!(pgdbport = ast_strdup(tmp))) {
00455       ast_config_destroy(cfg);
00456       return -1;
00457    }
00458 
00459    if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00460       ast_log(LOG_WARNING, "CDR table not specified.  Assuming cdr\n");
00461       tmp = "cdr";
00462    }
00463 
00464    if (table)
00465       ast_free(table);
00466    if (!(table = ast_strdup(tmp))) {
00467       ast_config_destroy(cfg);
00468       return -1;
00469    }
00470 
00471    if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00472       ast_log(LOG_WARNING, "Encoding not specified.  Assuming LATIN9\n");
00473       tmp = "LATIN9";
00474    }
00475 
00476    if (encoding) {
00477       ast_free(encoding);
00478    }
00479    if (!(encoding = ast_strdup(tmp))) {
00480       ast_config_destroy(cfg);
00481       return -1;
00482    }
00483 
00484    if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
00485       tmp = "";
00486    }
00487 
00488    if (tz) {
00489       ast_free(tz);
00490       tz = NULL;
00491    }
00492    if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
00493       ast_config_destroy(cfg);
00494       return -1;
00495    }
00496 
00497    if (option_debug) {
00498       if (ast_strlen_zero(pghostname)) {
00499          ast_debug(1, "using default unix socket\n");
00500       } else {
00501          ast_debug(1, "got hostname of %s\n", pghostname);
00502       }
00503       ast_debug(1, "got port of %s\n", pgdbport);
00504       ast_debug(1, "got user of %s\n", pgdbuser);
00505       ast_debug(1, "got dbname of %s\n", pgdbname);
00506       ast_debug(1, "got password of %s\n", pgpassword);
00507       ast_debug(1, "got sql table name of %s\n", table);
00508       ast_debug(1, "got encoding of %s\n", encoding);
00509       ast_debug(1, "got timezone of %s\n", tz);
00510    }
00511 
00512    conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00513    if (PQstatus(conn) != CONNECTION_BAD) {
00514       char sqlcmd[768];
00515       char *fname, *ftype, *flen, *fnotnull, *fdef;
00516       int i, rows, version;
00517       ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00518       connected = 1;
00519       if (PQsetClientEncoding(conn, encoding)) {
00520 #ifdef HAVE_PGSQL_pg_encoding_to_char
00521          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00522 #else
00523          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00524 #endif
00525       }
00526       version = PQserverVersion(conn);
00527 
00528       if (version >= 70300) {
00529          char *schemaname, *tablename;
00530          if (strchr(table, '.')) {
00531             schemaname = ast_strdupa(table);
00532             tablename = strchr(schemaname, '.');
00533             *tablename++ = '\0';
00534          } else {
00535             schemaname = "";
00536             tablename = table;
00537          }
00538 
00539          /* Escape special characters in schemaname */
00540          if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00541             char *tmp = schemaname, *ptr;
00542 
00543             ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00544             for (; *tmp; tmp++) {
00545                if (strchr("\\'", *tmp)) {
00546                   *ptr++ = *tmp;
00547                }
00548                *ptr++ = *tmp;
00549             }
00550             *ptr = '\0';
00551          }
00552          /* Escape special characters in tablename */
00553          if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00554             char *tmp = tablename, *ptr;
00555 
00556             ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00557             for (; *tmp; tmp++) {
00558                if (strchr("\\'", *tmp)) {
00559                   *ptr++ = *tmp;
00560                }
00561                *ptr++ = *tmp;
00562             }
00563             *ptr = '\0';
00564          }
00565 
00566          snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
00567             tablename,
00568             ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00569       } else {
00570          snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", table);
00571       }
00572       /* Query the columns */
00573       result = PQexec(conn, sqlcmd);
00574       if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00575          pgerror = PQresultErrorMessage(result);
00576          ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00577          PQclear(result);
00578          unload_module();
00579          return AST_MODULE_LOAD_DECLINE;
00580       }
00581 
00582       rows = PQntuples(result);
00583       if (rows == 0) {
00584          ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00585          PQclear(result);
00586          unload_module();
00587          return AST_MODULE_LOAD_DECLINE;
00588       }
00589 
00590       for (i = 0; i < rows; i++) {
00591          fname = PQgetvalue(result, i, 0);
00592          ftype = PQgetvalue(result, i, 1);
00593          flen = PQgetvalue(result, i, 2);
00594          fnotnull = PQgetvalue(result, i, 3);
00595          fdef = PQgetvalue(result, i, 4);
00596          if (atoi(flen) == -1) {
00597             /* For varchar columns, the maximum length is encoded in a different field */
00598             flen = PQgetvalue(result, i, 5);
00599          }
00600          ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00601          cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00602          if (cur) {
00603             sscanf(flen, "%30d", &cur->len);
00604             cur->name = (char *)cur + sizeof(*cur);
00605             cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00606             strcpy(cur->name, fname);
00607             strcpy(cur->type, ftype);
00608             if (*fnotnull == 't') {
00609                cur->notnull = 1;
00610             } else {
00611                cur->notnull = 0;
00612             }
00613             if (!ast_strlen_zero(fdef)) {
00614                cur->hasdefault = 1;
00615             } else {
00616                cur->hasdefault = 0;
00617             }
00618             AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00619          }
00620       }
00621       PQclear(result);
00622    } else {
00623       pgerror = PQerrorMessage(conn);
00624       ast_log(LOG_ERROR, "Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
00625       ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00626       connected = 0;
00627    }
00628 
00629    ast_config_destroy(cfg);
00630 
00631    return ast_cdr_register(name, ast_module_info->description, pgsql_log);
00632 }

static int load_module ( void   )  [static]

Definition at line 634 of file cdr_pgsql.c.

References AST_MODULE_LOAD_DECLINE, and config_module().

00635 {
00636    return config_module(0) ? AST_MODULE_LOAD_DECLINE : 0;
00637 }

static int pgsql_log ( struct ast_cdr cdr  )  [static]

Definition at line 101 of file cdr_pgsql.c.

References ast_cdr::answer, ast_cdr_getvar(), ast_debug, ast_free, ast_localtime(), ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_strlen(), ast_strftime(), ast_tvdiff_us(), ast_tvzero(), ast_verb, DATE_FORMAT, ast_cdr::end, first, columns::hasdefault, LENGTHEN_BUF1, LENGTHEN_BUF2, columns::list, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::notnull, pgsql_lock, ast_cdr::start, columns::type, and value.

Referenced by config_module().

00102 {
00103    struct ast_tm tm;
00104    char *pgerror;
00105    PGresult *result;
00106 
00107    ast_mutex_lock(&pgsql_lock);
00108 
00109    if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00110       conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00111       if (PQstatus(conn) != CONNECTION_BAD) {
00112          connected = 1;
00113          if (PQsetClientEncoding(conn, encoding)) {
00114 #ifdef HAVE_PGSQL_pg_encoding_to_char
00115             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00116 #else
00117             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00118 #endif
00119          }
00120       } else {
00121          pgerror = PQerrorMessage(conn);
00122          ast_log(LOG_ERROR, "Unable to connect to database server %s.  Calls will not be logged!\n", pghostname);
00123          ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00124          PQfinish(conn);
00125          conn = NULL;
00126       }
00127    }
00128 
00129    if (connected) {
00130       struct columns *cur;
00131       struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00132       char buf[257], escapebuf[513], *value;
00133       int first = 1;
00134   
00135       if (!sql || !sql2) {
00136          if (sql) {
00137             ast_free(sql);
00138          }
00139          if (sql2) {
00140             ast_free(sql2);
00141          }
00142          return -1;
00143       }
00144 
00145       ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00146       ast_str_set(&sql2, 0, " VALUES (");
00147 
00148       AST_RWLIST_RDLOCK(&psql_columns);
00149       AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00150          /* For fields not set, simply skip them */
00151          ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00152          if (strcmp(cur->name, "calldate") == 0 && !value) {
00153             ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00154          }
00155          if (!value) {
00156             if (cur->notnull && !cur->hasdefault) {
00157                /* Field is NOT NULL (but no default), must include it anyway */
00158                LENGTHEN_BUF1(strlen(cur->name) + 2);
00159                ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00160                LENGTHEN_BUF2(3);
00161                ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00162                first = 0;
00163             }
00164             continue;
00165          }
00166 
00167          LENGTHEN_BUF1(strlen(cur->name) + 2);
00168          ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00169 
00170          if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00171             if (strncmp(cur->type, "int", 3) == 0) {
00172                LENGTHEN_BUF2(13);
00173                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00174             } else if (strncmp(cur->type, "float", 5) == 0) {
00175                LENGTHEN_BUF2(31);
00176                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00177             } else {
00178                /* char, hopefully */
00179                LENGTHEN_BUF2(31);
00180                ast_localtime(&cdr->start, &tm, tz);
00181                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00182                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00183             }
00184          } else if (strcmp(cur->name, "answer") == 0) {
00185             if (strncmp(cur->type, "int", 3) == 0) {
00186                LENGTHEN_BUF2(13);
00187                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00188             } else if (strncmp(cur->type, "float", 5) == 0) {
00189                LENGTHEN_BUF2(31);
00190                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00191             } else {
00192                /* char, hopefully */
00193                LENGTHEN_BUF2(31);
00194                ast_localtime(&cdr->answer, &tm, tz);
00195                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00196                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00197             }
00198          } else if (strcmp(cur->name, "end") == 0) {
00199             if (strncmp(cur->type, "int", 3) == 0) {
00200                LENGTHEN_BUF2(13);
00201                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00202             } else if (strncmp(cur->type, "float", 5) == 0) {
00203                LENGTHEN_BUF2(31);
00204                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00205             } else {
00206                /* char, hopefully */
00207                LENGTHEN_BUF2(31);
00208                ast_localtime(&cdr->end, &tm, tz);
00209                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00210                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00211             }
00212          } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00213             if (cur->type[0] == 'i') {
00214                /* Get integer, no need to escape anything */
00215                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00216                LENGTHEN_BUF2(13);
00217                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00218             } else if (strncmp(cur->type, "float", 5) == 0) {
00219                struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00220                LENGTHEN_BUF2(31);
00221                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00222             } else {
00223                /* Char field, probably */
00224                struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00225                LENGTHEN_BUF2(31);
00226                ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00227             }
00228          } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00229             if (strncmp(cur->type, "int", 3) == 0) {
00230                /* Integer, no need to escape anything */
00231                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00232                LENGTHEN_BUF2(13);
00233                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00234             } else {
00235                /* Although this is a char field, there are no special characters in the values for these fields */
00236                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00237                LENGTHEN_BUF2(31);
00238                ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00239             }
00240          } else {
00241             /* Arbitrary field, could be anything */
00242             ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00243             if (strncmp(cur->type, "int", 3) == 0) {
00244                long long whatever;
00245                if (value && sscanf(value, "%30lld", &whatever) == 1) {
00246                   LENGTHEN_BUF2(26);
00247                   ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00248                } else {
00249                   LENGTHEN_BUF2(2);
00250                   ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00251                }
00252             } else if (strncmp(cur->type, "float", 5) == 0) {
00253                long double whatever;
00254                if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00255                   LENGTHEN_BUF2(51);
00256                   ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00257                } else {
00258                   LENGTHEN_BUF2(2);
00259                   ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00260                }
00261             /* XXX Might want to handle dates, times, and other misc fields here XXX */
00262             } else {
00263                if (value)
00264                   PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00265                else
00266                   escapebuf[0] = '\0';
00267                LENGTHEN_BUF2(strlen(escapebuf) + 3);
00268                ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00269             }
00270          }
00271          first = 0;
00272       }
00273       AST_RWLIST_UNLOCK(&psql_columns);
00274       LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00275       ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
00276       ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00277 
00278       ast_debug(2, "inserting a CDR record.\n");
00279 
00280       /* Test to be sure we're still connected... */
00281       /* If we're connected, and connection is working, good. */
00282       /* Otherwise, attempt reconnect.  If it fails... sorry... */
00283       if (PQstatus(conn) == CONNECTION_OK) {
00284          connected = 1;
00285       } else {
00286          ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
00287          PQreset(conn);
00288          if (PQstatus(conn) == CONNECTION_OK) {
00289             ast_log(LOG_ERROR, "Connection reestablished.\n");
00290             connected = 1;
00291          } else {
00292             pgerror = PQerrorMessage(conn);
00293             ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00294             ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00295             PQfinish(conn);
00296             conn = NULL;
00297             connected = 0;
00298             ast_mutex_unlock(&pgsql_lock);
00299             ast_free(sql);
00300             ast_free(sql2);
00301             return -1;
00302          }
00303       }
00304       result = PQexec(conn, ast_str_buffer(sql));
00305       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00306          pgerror = PQresultErrorMessage(result);
00307          ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
00308          ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00309          ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
00310          PQreset(conn);
00311          if (PQstatus(conn) == CONNECTION_OK) {
00312             ast_log(LOG_ERROR, "Connection reestablished.\n");
00313             connected = 1;
00314             PQclear(result);
00315             result = PQexec(conn, ast_str_buffer(sql));
00316             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00317                pgerror = PQresultErrorMessage(result);
00318                ast_log(LOG_ERROR, "HARD ERROR!  Attempted reconnection failed.  DROPPING CALL RECORD!\n");
00319                ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00320             }
00321          }
00322          ast_mutex_unlock(&pgsql_lock);
00323          PQclear(result);
00324          ast_free(sql);
00325          ast_free(sql2);
00326          return -1;
00327       }
00328       PQclear(result);
00329       ast_free(sql);
00330       ast_free(sql2);
00331    }
00332    ast_mutex_unlock(&pgsql_lock);
00333    return 0;
00334 }

static int reload ( void   )  [static]

Definition at line 639 of file cdr_pgsql.c.

References config_module().

00640 {
00641    return config_module(1);
00642 }

static int unload_module ( void   )  [static]

Definition at line 336 of file cdr_pgsql.c.

References ast_cdr_unregister(), ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and columns::list.

00337 {
00338    struct columns *current;
00339 
00340    ast_cdr_unregister(name);
00341 
00342    PQfinish(conn);
00343 
00344    if (pghostname) {
00345       ast_free(pghostname);
00346    }
00347    if (pgdbname) {
00348       ast_free(pgdbname);
00349    }
00350    if (pgdbuser) {
00351       ast_free(pgdbuser);
00352    }
00353    if (pgpassword) {
00354       ast_free(pgpassword);
00355    }
00356    if (pgdbport) {
00357       ast_free(pgdbport);
00358    }
00359    if (table) {
00360       ast_free(table);
00361    }
00362    if (encoding) {
00363       ast_free(encoding);
00364    }
00365    if (tz) {
00366       ast_free(tz);
00367    }
00368 
00369    AST_RWLIST_WRLOCK(&psql_columns);
00370    while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00371       ast_free(current);
00372    }
00373    AST_RWLIST_UNLOCK(&psql_columns);
00374 
00375    return 0;
00376 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PostgreSQL CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static]

Definition at line 649 of file cdr_pgsql.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 649 of file cdr_pgsql.c.

const char config[] = "cdr_pgsql.conf" [static]

Definition at line 54 of file cdr_pgsql.c.

PGconn* conn = NULL [static]

Definition at line 61 of file cdr_pgsql.c.

int connected = 0 [static]

Definition at line 56 of file cdr_pgsql.c.

Referenced by aji_initialize(), ast_channel_connected_line_macro(), ast_channel_queue_connected_line_update(), ast_channel_set_connected_line(), ast_channel_update_connected_line(), ast_connected_line_build_data(), ast_connected_line_parse_data(), connectedline_write(), dial_exec_full(), feature_request_and_dial(), handle_request_invite(), handle_request_update(), handle_response_invite(), misdn_queue_connected_line_update(), and socket_process().

char * encoding = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

Referenced by check_header(), and custom_prepare().

int maxsize = 512 [static]

Definition at line 57 of file cdr_pgsql.c.

int maxsize2 = 512 [static]

Definition at line 57 of file cdr_pgsql.c.

const char name[] = "pgsql" [static]

Definition at line 53 of file cdr_pgsql.c.

char * pgdbname = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

char * pgdbport = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

char * pgdbuser = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

char* pghostname = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

char * pgpassword = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

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

Definition at line 59 of file cdr_pgsql.c.

Referenced by config_pgsql(), destroy_pgsql(), parse_config(), pgsql_log(), realtime_multi_pgsql(), realtime_pgsql(), require_pgsql(), store_pgsql(), unload_module(), update2_pgsql(), and update_pgsql().

char * table = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

char * tz = NULL [static]

Definition at line 55 of file cdr_pgsql.c.

Referenced by ast_get_indication_zone(), ast_tone_zone_ref(), ast_tone_zone_unref(), ast_unregister_indication_country(), ast_var_indications(), ast_var_indications_table(), complete_country(), complete_indications(), handle_cli_indication_add(), handle_cli_indication_remove(), handle_cli_indication_show(), and in_band_indication().


Generated on Mon Jun 27 16:51:04 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7