Mon Mar 19 11:30:37 2012

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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 52 of file cdr_pgsql.c.

#define LENGTHEN_BUF1 ( size   ) 

Definition at line 75 of file cdr_pgsql.c.

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 89 of file cdr_pgsql.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 650 of file cdr_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 650 of file cdr_pgsql.c.

static int config_module ( int  reload  )  [static]

Definition at line 379 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().

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

static int load_module ( void   )  [static]

Definition at line 635 of file cdr_pgsql.c.

References AST_MODULE_LOAD_DECLINE, and config_module().

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

static int pgsql_log ( struct ast_cdr cdr  )  [static]

Definition at line 102 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().

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

static int reload ( void   )  [static]

Definition at line 640 of file cdr_pgsql.c.

References config_module().

00641 {
00642    return config_module(1);
00643 }

static int unload_module ( void   )  [static]

Definition at line 337 of file cdr_pgsql.c.

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

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


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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static]

Definition at line 650 of file cdr_pgsql.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 650 of file cdr_pgsql.c.

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

Definition at line 55 of file cdr_pgsql.c.

PGconn* conn = NULL [static]

Definition at line 62 of file cdr_pgsql.c.

int connected = 0 [static]

Definition at line 57 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(), do_forward(), feature_request_and_dial(), handle_request_invite(), handle_request_update(), handle_response_invite(), misdn_queue_connected_line_update(), parked_call_exec(), sip_call(), socket_process(), and wait_for_winner().

char * encoding = NULL [static]

Definition at line 56 of file cdr_pgsql.c.

Referenced by check_header(), and custom_prepare().

int maxsize = 512 [static]

Definition at line 58 of file cdr_pgsql.c.

int maxsize2 = 512 [static]

Definition at line 58 of file cdr_pgsql.c.

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

Definition at line 54 of file cdr_pgsql.c.

char * pgdbname = NULL [static]

Definition at line 56 of file cdr_pgsql.c.

char * pgdbport = NULL [static]

Definition at line 56 of file cdr_pgsql.c.

char * pgdbuser = NULL [static]

Definition at line 56 of file cdr_pgsql.c.

char* pghostname = NULL [static]

Definition at line 56 of file cdr_pgsql.c.

char * pgpassword = NULL [static]

Definition at line 56 of file cdr_pgsql.c.

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

Definition at line 60 of file cdr_pgsql.c.

Referenced by config_pgsql(), destroy_pgsql(), find_table(), 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 56 of file cdr_pgsql.c.

char * tz = NULL [static]

Definition at line 56 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 Mar 19 11:30:37 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7