Thu Oct 11 06:34:56 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/cli.h"
#include "asterisk/module.h"

Include dependency graph for cdr_pgsql.c:

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 __fini_psql_columns (void)
static void __init_psql_columns (void)
static void __reg_module (void)
static void __unreg_module (void)
static int config_module (int reload)
static void empty_columns (void)
static char * handle_cdr_pgsql_status (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 Handle the CLI command cdr show pgsql status.
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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cdr_pgsql_status_cli []
static const char config [] = "cdr_pgsql.conf"
static PGconn * conn = NULL
static time_t connect_time = 0
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_MUTEX_INITIALIZER , NULL, 1 }
static int records
static char * table = NULL
static int totalrecords = 0
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 53 of file cdr_pgsql.c.

#define LENGTHEN_BUF1 ( size   ) 

Definition at line 84 of file cdr_pgsql.c.

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 99 of file cdr_pgsql.c.


Function Documentation

static void __fini_psql_columns ( void   )  [static]

Definition at line 82 of file cdr_pgsql.c.

00085 {                                                          \

static void __init_psql_columns ( void   )  [static]

Definition at line 82 of file cdr_pgsql.c.

00085 {                                                          \

static void __reg_module ( void   )  [static]

Definition at line 739 of file cdr_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 739 of file cdr_pgsql.c.

static int config_module ( int  reload  )  [static]

Definition at line 449 of file cdr_pgsql.c.

References ast_alloca, 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::hasdefault, columns::len, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, columns::notnull, option_debug, pgsql_lock, columns::type, unload_module, and version.

Referenced by load_module(), and reload().

00450 {
00451    char *pgerror;
00452    struct columns *cur;
00453    PGresult *result;
00454    const char *tmp;
00455    struct ast_config *cfg;
00456    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00457 
00458    if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00459       ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00460       return -1;
00461    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00462       return 0;
00463    }
00464 
00465    ast_mutex_lock(&pgsql_lock);
00466 
00467    if (!ast_variable_browse(cfg, "global")) {
00468       ast_config_destroy(cfg);
00469       ast_mutex_unlock(&pgsql_lock);
00470       ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
00471          reload ? "reload" : "load");
00472       return -1;
00473    }
00474 
00475    if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00476       ast_log(LOG_WARNING, "PostgreSQL server hostname not specified.  Assuming unix socket connection\n");
00477       tmp = "";   /* connect via UNIX-socket by default */
00478    }
00479 
00480    ast_free(pghostname);
00481    if (!(pghostname = ast_strdup(tmp))) {
00482       ast_config_destroy(cfg);
00483       ast_mutex_unlock(&pgsql_lock);
00484       return -1;
00485    }
00486 
00487    if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00488       ast_log(LOG_WARNING, "PostgreSQL database not specified.  Assuming asterisk\n");
00489       tmp = "asteriskcdrdb";
00490    }
00491 
00492    ast_free(pgdbname);
00493    if (!(pgdbname = ast_strdup(tmp))) {
00494       ast_config_destroy(cfg);
00495       ast_mutex_unlock(&pgsql_lock);
00496       return -1;
00497    }
00498 
00499    if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00500       ast_log(LOG_WARNING, "PostgreSQL database user not specified.  Assuming asterisk\n");
00501       tmp = "asterisk";
00502    }
00503 
00504    ast_free(pgdbuser);
00505    if (!(pgdbuser = ast_strdup(tmp))) {
00506       ast_config_destroy(cfg);
00507       ast_mutex_unlock(&pgsql_lock);
00508       return -1;
00509    }
00510 
00511    if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00512       ast_log(LOG_WARNING, "PostgreSQL database password not specified.  Assuming blank\n");
00513       tmp = "";
00514    }
00515 
00516    ast_free(pgpassword);
00517    if (!(pgpassword = ast_strdup(tmp))) {
00518       ast_config_destroy(cfg);
00519       ast_mutex_unlock(&pgsql_lock);
00520       return -1;
00521    }
00522 
00523    if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00524       ast_log(LOG_WARNING, "PostgreSQL database port not specified.  Using default 5432.\n");
00525       tmp = "5432";
00526    }
00527 
00528    ast_free(pgdbport);
00529    if (!(pgdbport = ast_strdup(tmp))) {
00530       ast_config_destroy(cfg);
00531       ast_mutex_unlock(&pgsql_lock);
00532       return -1;
00533    }
00534 
00535    if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00536       ast_log(LOG_WARNING, "CDR table not specified.  Assuming cdr\n");
00537       tmp = "cdr";
00538    }
00539 
00540    ast_free(table);
00541    if (!(table = ast_strdup(tmp))) {
00542       ast_config_destroy(cfg);
00543       ast_mutex_unlock(&pgsql_lock);
00544       return -1;
00545    }
00546 
00547    if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00548       ast_log(LOG_WARNING, "Encoding not specified.  Assuming LATIN9\n");
00549       tmp = "LATIN9";
00550    }
00551 
00552    ast_free(encoding);
00553    if (!(encoding = ast_strdup(tmp))) {
00554       ast_config_destroy(cfg);
00555       ast_mutex_unlock(&pgsql_lock);
00556       return -1;
00557    }
00558 
00559    if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
00560       tmp = "";
00561    }
00562 
00563    ast_free(tz);
00564    tz = NULL;
00565 
00566    if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
00567       ast_config_destroy(cfg);
00568       ast_mutex_unlock(&pgsql_lock);
00569       return -1;
00570    }
00571 
00572    if (option_debug) {
00573       if (ast_strlen_zero(pghostname)) {
00574          ast_debug(1, "using default unix socket\n");
00575       } else {
00576          ast_debug(1, "got hostname of %s\n", pghostname);
00577       }
00578       ast_debug(1, "got port of %s\n", pgdbport);
00579       ast_debug(1, "got user of %s\n", pgdbuser);
00580       ast_debug(1, "got dbname of %s\n", pgdbname);
00581       ast_debug(1, "got password of %s\n", pgpassword);
00582       ast_debug(1, "got sql table name of %s\n", table);
00583       ast_debug(1, "got encoding of %s\n", encoding);
00584       ast_debug(1, "got timezone of %s\n", tz);
00585    }
00586 
00587    conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00588    if (PQstatus(conn) != CONNECTION_BAD) {
00589       char sqlcmd[768];
00590       char *fname, *ftype, *flen, *fnotnull, *fdef;
00591       int i, rows, version;
00592       ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00593       connected = 1;
00594       connect_time = time(NULL);
00595       records = 0;
00596       if (PQsetClientEncoding(conn, encoding)) {
00597 #ifdef HAVE_PGSQL_pg_encoding_to_char
00598          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00599 #else
00600          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00601 #endif
00602       }
00603       version = PQserverVersion(conn);
00604 
00605       if (version >= 70300) {
00606          char *schemaname, *tablename;
00607          if (strchr(table, '.')) {
00608             schemaname = ast_strdupa(table);
00609             tablename = strchr(schemaname, '.');
00610             *tablename++ = '\0';
00611          } else {
00612             schemaname = "";
00613             tablename = table;
00614          }
00615 
00616          /* Escape special characters in schemaname */
00617          if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00618             char *tmp = schemaname, *ptr;
00619 
00620             ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
00621             for (; *tmp; tmp++) {
00622                if (strchr("\\'", *tmp)) {
00623                   *ptr++ = *tmp;
00624                }
00625                *ptr++ = *tmp;
00626             }
00627             *ptr = '\0';
00628          }
00629          /* Escape special characters in tablename */
00630          if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00631             char *tmp = tablename, *ptr;
00632 
00633             ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
00634             for (; *tmp; tmp++) {
00635                if (strchr("\\'", *tmp)) {
00636                   *ptr++ = *tmp;
00637                }
00638                *ptr++ = *tmp;
00639             }
00640             *ptr = '\0';
00641          }
00642 
00643          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",
00644             tablename,
00645             ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00646       } else {
00647          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);
00648       }
00649       /* Query the columns */
00650       result = PQexec(conn, sqlcmd);
00651       if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00652          pgerror = PQresultErrorMessage(result);
00653          ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00654          PQclear(result);
00655          unload_module();
00656          ast_mutex_unlock(&pgsql_lock);
00657          return AST_MODULE_LOAD_DECLINE;
00658       }
00659 
00660       rows = PQntuples(result);
00661       if (rows == 0) {
00662          ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00663          PQclear(result);
00664          unload_module();
00665          ast_mutex_unlock(&pgsql_lock);
00666          return AST_MODULE_LOAD_DECLINE;
00667       }
00668 
00669       /* Clear out the columns list. */
00670       empty_columns();
00671 
00672       for (i = 0; i < rows; i++) {
00673          fname = PQgetvalue(result, i, 0);
00674          ftype = PQgetvalue(result, i, 1);
00675          flen = PQgetvalue(result, i, 2);
00676          fnotnull = PQgetvalue(result, i, 3);
00677          fdef = PQgetvalue(result, i, 4);
00678          if (atoi(flen) == -1) {
00679             /* For varchar columns, the maximum length is encoded in a different field */
00680             flen = PQgetvalue(result, i, 5);
00681          }
00682          ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00683          cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00684          if (cur) {
00685             sscanf(flen, "%30d", &cur->len);
00686             cur->name = (char *)cur + sizeof(*cur);
00687             cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00688             strcpy(cur->name, fname);
00689             strcpy(cur->type, ftype);
00690             if (*fnotnull == 't') {
00691                cur->notnull = 1;
00692             } else {
00693                cur->notnull = 0;
00694             }
00695             if (!ast_strlen_zero(fdef)) {
00696                cur->hasdefault = 1;
00697             } else {
00698                cur->hasdefault = 0;
00699             }
00700             AST_RWLIST_WRLOCK(&psql_columns);
00701             AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00702             AST_RWLIST_UNLOCK(&psql_columns);
00703          }
00704       }
00705       PQclear(result);
00706    } else {
00707       pgerror = PQerrorMessage(conn);
00708       ast_log(LOG_ERROR, "Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
00709       ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00710       connected = 0;
00711    }
00712 
00713    ast_config_destroy(cfg);
00714 
00715    ast_mutex_unlock(&pgsql_lock);
00716    return 0;
00717 }

static void empty_columns ( void   )  [static]

Definition at line 417 of file cdr_pgsql.c.

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, and AST_RWLIST_WRLOCK.

Referenced by config_module(), and unload_module().

00418 {
00419    struct columns *current;
00420    AST_RWLIST_WRLOCK(&psql_columns);
00421    while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00422       ast_free(current);
00423    }
00424    AST_RWLIST_UNLOCK(&psql_columns);
00425 
00426 }

static char * handle_cdr_pgsql_status ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Handle the CLI command cdr show pgsql status.

Definition at line 114 of file cdr_pgsql.c.

References ast_cli_args::argc, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, status, and ast_cli_entry::usage.

00115 {
00116    switch (cmd) {
00117    case CLI_INIT:
00118       e->command = "cdr show pgsql status";
00119       e->usage =
00120          "Usage: cdr show pgsql status\n"
00121          "       Shows current connection status for cdr_pgsql\n";
00122       return NULL;
00123    case CLI_GENERATE:
00124       return NULL;
00125    }
00126 
00127    if (a->argc != 3)
00128       return CLI_SHOWUSAGE;
00129 
00130    if (connected) {
00131       char status[256], status2[100] = "";
00132       int ctime = time(NULL) - connect_time;
00133 
00134       if (pgdbport) {
00135          snprintf(status, 255, "Connected to %s@%s, port %s", pgdbname, pghostname, pgdbport);
00136       } else {
00137          snprintf(status, 255, "Connected to %s@%s", pgdbname, pghostname);
00138       }
00139 
00140       if (pgdbuser && *pgdbuser) {
00141          snprintf(status2, 99, " with username %s", pgdbuser);
00142       }
00143       if (table && *table) {
00144          snprintf(status2, 99, " using table %s", table);
00145       }
00146       if (ctime > 31536000) {
00147          ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00148       } else if (ctime > 86400) {
00149          ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00150       } else if (ctime > 3600) {
00151          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00152       } else if (ctime > 60) {
00153          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
00154       } else {
00155          ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
00156       }
00157       if (records == totalrecords) {
00158          ast_cli(a->fd, "  Wrote %d records since last restart.\n", totalrecords);
00159       } else {
00160          ast_cli(a->fd, "  Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
00161       }
00162    } else {
00163       ast_cli(a->fd, "Not currently connected to a PgSQL server.\n");
00164    }
00165    return CLI_SUCCESS;
00166 }

static int load_module ( void   )  [static]

Definition at line 719 of file cdr_pgsql.c.

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

00720 {
00721    ast_cli_register_multiple(cdr_pgsql_status_cli, sizeof(cdr_pgsql_status_cli) / sizeof(struct ast_cli_entry));
00722    if (config_module(0)) {
00723       return AST_MODULE_LOAD_DECLINE;
00724    }
00725    return ast_cdr_register(name, ast_module_info->description, pgsql_log)
00726       ? AST_MODULE_LOAD_DECLINE : 0;
00727 }

static int pgsql_log ( struct ast_cdr cdr  )  [static]

Definition at line 168 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, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::notnull, pgsql_lock, ast_cdr::start, columns::type, and value.

Referenced by load_module().

00169 {
00170    struct ast_tm tm;
00171    char *pgerror;
00172    PGresult *result;
00173 
00174    ast_mutex_lock(&pgsql_lock);
00175 
00176    if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00177       conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00178       if (PQstatus(conn) != CONNECTION_BAD) {
00179          connected = 1;
00180          connect_time = time(NULL);
00181          records = 0;
00182          if (PQsetClientEncoding(conn, encoding)) {
00183 #ifdef HAVE_PGSQL_pg_encoding_to_char
00184             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00185 #else
00186             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00187 #endif
00188          }
00189       } else {
00190          pgerror = PQerrorMessage(conn);
00191          ast_log(LOG_ERROR, "Unable to connect to database server %s.  Calls will not be logged!\n", pghostname);
00192          ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00193          PQfinish(conn);
00194          conn = NULL;
00195       }
00196    }
00197 
00198    if (connected) {
00199       struct columns *cur;
00200       struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00201       char buf[257], escapebuf[513], *value;
00202       int first = 1;
00203 
00204       if (!sql || !sql2) {
00205          ast_free(sql);
00206          ast_free(sql2);
00207          return -1;
00208       }
00209 
00210       ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00211       ast_str_set(&sql2, 0, " VALUES (");
00212 
00213       AST_RWLIST_RDLOCK(&psql_columns);
00214       AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00215          /* For fields not set, simply skip them */
00216          ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00217          if (strcmp(cur->name, "calldate") == 0 && !value) {
00218             ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00219          }
00220          if (!value) {
00221             if (cur->notnull && !cur->hasdefault) {
00222                /* Field is NOT NULL (but no default), must include it anyway */
00223                LENGTHEN_BUF1(strlen(cur->name) + 2);
00224                ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00225                LENGTHEN_BUF2(3);
00226                ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00227                first = 0;
00228             }
00229             continue;
00230          }
00231 
00232          LENGTHEN_BUF1(strlen(cur->name) + 2);
00233          ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00234 
00235          if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00236             if (strncmp(cur->type, "int", 3) == 0) {
00237                LENGTHEN_BUF2(13);
00238                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00239             } else if (strncmp(cur->type, "float", 5) == 0) {
00240                LENGTHEN_BUF2(31);
00241                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00242             } else {
00243                /* char, hopefully */
00244                LENGTHEN_BUF2(31);
00245                ast_localtime(&cdr->start, &tm, tz);
00246                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00247                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00248             }
00249          } else if (strcmp(cur->name, "answer") == 0) {
00250             if (strncmp(cur->type, "int", 3) == 0) {
00251                LENGTHEN_BUF2(13);
00252                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00253             } else if (strncmp(cur->type, "float", 5) == 0) {
00254                LENGTHEN_BUF2(31);
00255                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00256             } else {
00257                /* char, hopefully */
00258                LENGTHEN_BUF2(31);
00259                ast_localtime(&cdr->answer, &tm, tz);
00260                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00261                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00262             }
00263          } else if (strcmp(cur->name, "end") == 0) {
00264             if (strncmp(cur->type, "int", 3) == 0) {
00265                LENGTHEN_BUF2(13);
00266                ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00267             } else if (strncmp(cur->type, "float", 5) == 0) {
00268                LENGTHEN_BUF2(31);
00269                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00270             } else {
00271                /* char, hopefully */
00272                LENGTHEN_BUF2(31);
00273                ast_localtime(&cdr->end, &tm, tz);
00274                ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00275                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00276             }
00277          } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00278             if (cur->type[0] == 'i') {
00279                /* Get integer, no need to escape anything */
00280                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00281                LENGTHEN_BUF2(13);
00282                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00283             } else if (strncmp(cur->type, "float", 5) == 0) {
00284                struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00285                LENGTHEN_BUF2(31);
00286                ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00287             } else {
00288                /* Char field, probably */
00289                struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00290                LENGTHEN_BUF2(31);
00291                ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00292             }
00293          } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00294             if (strncmp(cur->type, "int", 3) == 0) {
00295                /* Integer, no need to escape anything */
00296                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00297                LENGTHEN_BUF2(13);
00298                ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00299             } else {
00300                /* Although this is a char field, there are no special characters in the values for these fields */
00301                ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00302                LENGTHEN_BUF2(31);
00303                ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00304             }
00305          } else {
00306             /* Arbitrary field, could be anything */
00307             ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00308             if (strncmp(cur->type, "int", 3) == 0) {
00309                long long whatever;
00310                if (value && sscanf(value, "%30lld", &whatever) == 1) {
00311                   LENGTHEN_BUF2(26);
00312                   ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00313                } else {
00314                   LENGTHEN_BUF2(2);
00315                   ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00316                }
00317             } else if (strncmp(cur->type, "float", 5) == 0) {
00318                long double whatever;
00319                if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00320                   LENGTHEN_BUF2(51);
00321                   ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00322                } else {
00323                   LENGTHEN_BUF2(2);
00324                   ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00325                }
00326             /* XXX Might want to handle dates, times, and other misc fields here XXX */
00327             } else {
00328                if (value)
00329                   PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00330                else
00331                   escapebuf[0] = '\0';
00332                LENGTHEN_BUF2(strlen(escapebuf) + 3);
00333                ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00334             }
00335          }
00336          first = 0;
00337       }
00338 
00339       LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00340       AST_RWLIST_UNLOCK(&psql_columns);
00341       ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
00342       ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00343 
00344       ast_debug(2, "inserting a CDR record.\n");
00345 
00346       /* Test to be sure we're still connected... */
00347       /* If we're connected, and connection is working, good. */
00348       /* Otherwise, attempt reconnect.  If it fails... sorry... */
00349       if (PQstatus(conn) == CONNECTION_OK) {
00350          connected = 1;
00351       } else {
00352          ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
00353          PQreset(conn);
00354          if (PQstatus(conn) == CONNECTION_OK) {
00355             ast_log(LOG_ERROR, "Connection reestablished.\n");
00356             connected = 1;
00357             connect_time = time(NULL);
00358             records = 0;
00359          } else {
00360             pgerror = PQerrorMessage(conn);
00361             ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00362             ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00363             PQfinish(conn);
00364             conn = NULL;
00365             connected = 0;
00366             ast_mutex_unlock(&pgsql_lock);
00367             ast_free(sql);
00368             ast_free(sql2);
00369             return -1;
00370          }
00371       }
00372       result = PQexec(conn, ast_str_buffer(sql));
00373       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00374          pgerror = PQresultErrorMessage(result);
00375          ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
00376          ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00377          ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
00378          PQreset(conn);
00379          if (PQstatus(conn) == CONNECTION_OK) {
00380             ast_log(LOG_ERROR, "Connection reestablished.\n");
00381             connected = 1;
00382             connect_time = time(NULL);
00383             records = 0;
00384             PQclear(result);
00385             result = PQexec(conn, ast_str_buffer(sql));
00386             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00387                pgerror = PQresultErrorMessage(result);
00388                ast_log(LOG_ERROR, "HARD ERROR!  Attempted reconnection failed.  DROPPING CALL RECORD!\n");
00389                ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00390             }  else {
00391                /* Second try worked out ok */
00392                totalrecords++;
00393                records++;
00394                ast_mutex_unlock(&pgsql_lock);
00395                PQclear(result);
00396                return 0;
00397             }
00398          }
00399          ast_mutex_unlock(&pgsql_lock);
00400          PQclear(result);
00401          ast_free(sql);
00402          ast_free(sql2);
00403          return -1;
00404       } else {
00405          totalrecords++;
00406          records++;
00407       }
00408       PQclear(result);
00409       ast_free(sql);
00410       ast_free(sql2);
00411    }
00412    ast_mutex_unlock(&pgsql_lock);
00413    return 0;
00414 }

static int reload ( void   )  [static]

Definition at line 729 of file cdr_pgsql.c.

References config_module().

00730 {
00731    return config_module(1);
00732 }

static int unload_module ( void   )  [static]

Definition at line 428 of file cdr_pgsql.c.

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

00429 {
00430    ast_cdr_unregister(name);
00431    ast_cli_unregister_multiple(cdr_pgsql_status_cli, ARRAY_LEN(cdr_pgsql_status_cli));
00432 
00433    PQfinish(conn);
00434 
00435    ast_free(pghostname);
00436    ast_free(pgdbname);
00437    ast_free(pgdbuser);
00438    ast_free(pgpassword);
00439    ast_free(pgdbport);
00440    ast_free(table);
00441    ast_free(encoding);
00442    ast_free(tz);
00443 
00444    empty_columns();
00445 
00446    return 0;
00447 }


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

Definition at line 739 of file cdr_pgsql.c.

Definition at line 739 of file cdr_pgsql.c.

Initial value:

 {
        AST_CLI_DEFINE(handle_cdr_pgsql_status, "Show connection status of the PostgreSQL CDR driver (cdr_pgsql)"),
}

Definition at line 65 of file cdr_pgsql.c.

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

Definition at line 56 of file cdr_pgsql.c.

PGconn* conn = NULL [static]

Definition at line 71 of file cdr_pgsql.c.

time_t connect_time = 0 [static]

Definition at line 60 of file cdr_pgsql.c.

Referenced by handle_cli_realtime_pgsql_status(), and pgsql_reconnect().

int connected = 0 [static]

char * encoding = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

Referenced by check_header(), and custom_prepare().

int maxsize = 512 [static]

Definition at line 59 of file cdr_pgsql.c.

int maxsize2 = 512 [static]

Definition at line 59 of file cdr_pgsql.c.

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

Definition at line 55 of file cdr_pgsql.c.

char * pgdbname = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

char * pgdbport = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

char * pgdbuser = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

char* pghostname = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

char * pgpassword = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

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

int records [static]

Definition at line 62 of file cdr_pgsql.c.

char * table = NULL [static]

Definition at line 57 of file cdr_pgsql.c.

int totalrecords = 0 [static]

Definition at line 61 of file cdr_pgsql.c.

char * tz = NULL [static]


Generated on Thu Oct 11 06:34:56 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6