Mon Oct 8 12:39:12 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 void empty_columns (void)
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 = "ac1f6a56484a8820659555499174e588" , .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 90 of file cdr_pgsql.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 656 of file cdr_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 656 of file cdr_pgsql.c.

static int config_module ( int  reload  )  [static]

Definition at line 368 of file cdr_pgsql.c.

References ast_calloc, ast_config_destroy(), ast_config_load, ast_debug, ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, empty_columns(), columns::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, option_debug, pgsql_lock, unload_module, var, and version.

Referenced by load_module(), and reload().

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

static void empty_columns ( void   )  [static]

Definition at line 337 of file cdr_pgsql.c.

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, and columns::list.

Referenced by config_module(), and unload_module().

00338 {
00339    struct columns *current;
00340    AST_RWLIST_WRLOCK(&psql_columns);
00341    while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00342       ast_free(current);
00343    }
00344    AST_RWLIST_UNLOCK(&psql_columns);
00345 
00346 }

static int load_module ( void   )  [static]

Definition at line 637 of file cdr_pgsql.c.

References ast_cdr_register(), AST_MODULE_LOAD_DECLINE, config_module(), and pgsql_log().

00638 {
00639    if (config_module(0)) {
00640       return AST_MODULE_LOAD_DECLINE;
00641    }
00642    return ast_cdr_register(name, ast_module_info->description, pgsql_log)
00643       ? AST_MODULE_LOAD_DECLINE : 0;
00644 }

static int pgsql_log ( struct ast_cdr cdr  )  [static]

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

00105 {
00106    struct ast_tm tm;
00107    char *pgerror;
00108    PGresult *result;
00109 
00110    ast_mutex_lock(&pgsql_lock);
00111 
00112    if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00113       conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00114       if (PQstatus(conn) != CONNECTION_BAD) {
00115          connected = 1;
00116          if (PQsetClientEncoding(conn, encoding)) {
00117 #ifdef HAVE_PGSQL_pg_encoding_to_char
00118             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00119 #else
00120             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00121 #endif
00122          }
00123       } else {
00124          pgerror = PQerrorMessage(conn);
00125          ast_log(LOG_ERROR, "Unable to connect to database server %s.  Calls will not be logged!\n", pghostname);
00126          ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00127          PQfinish(conn);
00128          conn = NULL;
00129       }
00130    }
00131 
00132    if (connected) {
00133       struct columns *cur;
00134       struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00135       char buf[257], escapebuf[513], *value;
00136       int first = 1;
00137 
00138       if (!sql || !sql2) {
00139          ast_free(sql);
00140          ast_free(sql2);
00141          return -1;
00142       }
00143 
00144       ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00145       ast_str_set(&sql2, 0, " VALUES (");
00146 
00147       AST_RWLIST_RDLOCK(&psql_columns);
00148       AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00149          /* For fields not set, simply skip them */
00150          ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00151          if (strcmp(cur->name, "calldate") == 0 && !value) {
00152             ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00153          }
00154          if (!value) {
00155             if (cur->notnull && !cur->hasdefault) {
00156                /* Field is NOT NULL (but no default), must include it anyway */
00157                LENGTHEN_BUF1(strlen(cur->name) + 2);
00158                ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00159                LENGTHEN_BUF2(3);
00160                ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00161                first = 0;
00162             }
00163             continue;
00164          }
00165 
00166          LENGTHEN_BUF1(strlen(cur->name) + 2);
00167          ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00168 
00169          if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00170             if (strncmp(cur->type, "int", 3) == 0) {
00171                LENGTHEN_BUF2(13);
00172                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00173             } else if (strncmp(cur->type, "float", 5) == 0) {
00174                LENGTHEN_BUF2(31);
00175                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00176             } else {
00177                /* char, hopefully */
00178                LENGTHEN_BUF2(31);
00179                ast_localtime(&cdr->start, &tm, tz);
00180                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00181                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00182             }
00183          } else if (strcmp(cur->name, "answer") == 0) {
00184             if (strncmp(cur->type, "int", 3) == 0) {
00185                LENGTHEN_BUF2(13);
00186                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00187             } else if (strncmp(cur->type, "float", 5) == 0) {
00188                LENGTHEN_BUF2(31);
00189                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00190             } else {
00191                /* char, hopefully */
00192                LENGTHEN_BUF2(31);
00193                ast_localtime(&cdr->answer, &tm, tz);
00194                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00195                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00196             }
00197          } else if (strcmp(cur->name, "end") == 0) {
00198             if (strncmp(cur->type, "int", 3) == 0) {
00199                LENGTHEN_BUF2(13);
00200                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00201             } else if (strncmp(cur->type, "float", 5) == 0) {
00202                LENGTHEN_BUF2(31);
00203                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00204             } else {
00205                /* char, hopefully */
00206                LENGTHEN_BUF2(31);
00207                ast_localtime(&cdr->end, &tm, tz);
00208                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00209                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00210             }
00211          } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00212             if (cur->type[0] == 'i') {
00213                /* Get integer, no need to escape anything */
00214                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00215                LENGTHEN_BUF2(13);
00216                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00217             } else if (strncmp(cur->type, "float", 5) == 0) {
00218                struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00219                LENGTHEN_BUF2(31);
00220                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00221             } else {
00222                /* Char field, probably */
00223                struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00224                LENGTHEN_BUF2(31);
00225                ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00226             }
00227          } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00228             if (strncmp(cur->type, "int", 3) == 0) {
00229                /* Integer, no need to escape anything */
00230                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00231                LENGTHEN_BUF2(13);
00232                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00233             } else {
00234                /* Although this is a char field, there are no special characters in the values for these fields */
00235                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00236                LENGTHEN_BUF2(31);
00237                ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00238             }
00239          } else {
00240             /* Arbitrary field, could be anything */
00241             ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00242             if (strncmp(cur->type, "int", 3) == 0) {
00243                long long whatever;
00244                if (value && sscanf(value, "%30lld", &whatever) == 1) {
00245                   LENGTHEN_BUF2(26);
00246                   ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00247                } else {
00248                   LENGTHEN_BUF2(2);
00249                   ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00250                }
00251             } else if (strncmp(cur->type, "float", 5) == 0) {
00252                long double whatever;
00253                if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00254                   LENGTHEN_BUF2(51);
00255                   ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00256                } else {
00257                   LENGTHEN_BUF2(2);
00258                   ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00259                }
00260             /* XXX Might want to handle dates, times, and other misc fields here XXX */
00261             } else {
00262                if (value)
00263                   PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00264                else
00265                   escapebuf[0] = '\0';
00266                LENGTHEN_BUF2(strlen(escapebuf) + 3);
00267                ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00268             }
00269          }
00270          first = 0;
00271       }
00272 
00273       LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00274       AST_RWLIST_UNLOCK(&psql_columns);
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 646 of file cdr_pgsql.c.

References config_module().

00647 {
00648    return config_module(1);
00649 }

static int unload_module ( void   )  [static]

Definition at line 348 of file cdr_pgsql.c.

References ast_cdr_unregister(), ast_free, and empty_columns().

00349 {
00350    ast_cdr_unregister(name);
00351 
00352    PQfinish(conn);
00353 
00354    ast_free(pghostname);
00355    ast_free(pgdbname);
00356    ast_free(pgdbuser);
00357    ast_free(pgpassword);
00358    ast_free(pgdbport);
00359    ast_free(table);
00360    ast_free(encoding);
00361    ast_free(tz);
00362 
00363    empty_columns();
00364 
00365    return 0;
00366 }


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

Definition at line 656 of file cdr_pgsql.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 656 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_module(), 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 Oct 8 12:39:12 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7