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 void pgsql_reconnect (void)
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, .support_level = AST_MODULE_SUPPORT_EXTENDED, .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
static int maxsize = 512
static int maxsize2 = 512
static const char name [] = "pgsql"
static char * pgappname
static char * pgdbname
static char * pgdbport
static char * pgdbuser
static char * pghostname
static char * pgpassword
static ast_mutex_t pgsql_lock = { PTHREAD_MUTEX_INITIALIZER , NULL, 1 }
static int records
static char * table
static int totalrecords = 0
static char * tz


Detailed Description

PostgreSQL CDR logger.

Author:
Matthew D. Hardeman <mhardemn@papersoft.com> 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 62 of file cdr_pgsql.c.

#define LENGTHEN_BUF1 ( size   ) 

Definition at line 103 of file cdr_pgsql.c.

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 118 of file cdr_pgsql.c.


Function Documentation

static void __fini_psql_columns ( void   )  [static]

Definition at line 101 of file cdr_pgsql.c.

00104 {                                                          \

static void __init_psql_columns ( void   )  [static]

Definition at line 101 of file cdr_pgsql.c.

00104 {                                                          \

static void __reg_module ( void   )  [static]

Definition at line 809 of file cdr_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 809 of file cdr_pgsql.c.

static int config_module ( int  reload  )  [static]

Definition at line 502 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(), 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, NULL, option_debug, pgsql_lock, pgsql_reconnect(), result, tmp(), columns::type, unload_module, and version.

Referenced by load_module(), and reload().

00503 {
00504    char *pgerror;
00505    struct columns *cur;
00506    PGresult *result;
00507    const char *tmp;
00508    struct ast_config *cfg;
00509    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00510 
00511    if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00512       ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00513       return -1;
00514    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00515       return 0;
00516    }
00517 
00518    ast_mutex_lock(&pgsql_lock);
00519 
00520    if (!ast_variable_browse(cfg, "global")) {
00521       ast_config_destroy(cfg);
00522       ast_mutex_unlock(&pgsql_lock);
00523       ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
00524          reload ? "reload" : "load");
00525       return -1;
00526    }
00527 
00528    if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00529       ast_log(LOG_WARNING, "PostgreSQL server hostname not specified.  Assuming unix socket connection\n");
00530       tmp = "";   /* connect via UNIX-socket by default */
00531    }
00532 
00533    ast_free(pghostname);
00534    if (!(pghostname = ast_strdup(tmp))) {
00535       ast_config_destroy(cfg);
00536       ast_mutex_unlock(&pgsql_lock);
00537       return -1;
00538    }
00539 
00540    if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00541       ast_log(LOG_WARNING, "PostgreSQL database not specified.  Assuming asterisk\n");
00542       tmp = "asteriskcdrdb";
00543    }
00544 
00545    ast_free(pgdbname);
00546    if (!(pgdbname = ast_strdup(tmp))) {
00547       ast_config_destroy(cfg);
00548       ast_mutex_unlock(&pgsql_lock);
00549       return -1;
00550    }
00551 
00552    if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00553       ast_log(LOG_WARNING, "PostgreSQL database user not specified.  Assuming asterisk\n");
00554       tmp = "asterisk";
00555    }
00556 
00557    ast_free(pgdbuser);
00558    if (!(pgdbuser = ast_strdup(tmp))) {
00559       ast_config_destroy(cfg);
00560       ast_mutex_unlock(&pgsql_lock);
00561       return -1;
00562    }
00563 
00564    if (!(tmp = ast_variable_retrieve(cfg, "global", "appname"))) {
00565       tmp = "";
00566    }
00567 
00568    ast_free(pgappname);
00569    if (!(pgappname = ast_strdup(tmp))) {
00570       ast_config_destroy(cfg);
00571       ast_mutex_unlock(&pgsql_lock);
00572       return -1;
00573    }
00574 
00575 
00576    if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00577       ast_log(LOG_WARNING, "PostgreSQL database password not specified.  Assuming blank\n");
00578       tmp = "";
00579    }
00580 
00581    ast_free(pgpassword);
00582    if (!(pgpassword = ast_strdup(tmp))) {
00583       ast_config_destroy(cfg);
00584       ast_mutex_unlock(&pgsql_lock);
00585       return -1;
00586    }
00587 
00588    if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00589       ast_log(LOG_WARNING, "PostgreSQL database port not specified.  Using default 5432.\n");
00590       tmp = "5432";
00591    }
00592 
00593    ast_free(pgdbport);
00594    if (!(pgdbport = ast_strdup(tmp))) {
00595       ast_config_destroy(cfg);
00596       ast_mutex_unlock(&pgsql_lock);
00597       return -1;
00598    }
00599 
00600    if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00601       ast_log(LOG_WARNING, "CDR table not specified.  Assuming cdr\n");
00602       tmp = "cdr";
00603    }
00604 
00605    ast_free(table);
00606    if (!(table = ast_strdup(tmp))) {
00607       ast_config_destroy(cfg);
00608       ast_mutex_unlock(&pgsql_lock);
00609       return -1;
00610    }
00611 
00612    if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00613       ast_log(LOG_WARNING, "Encoding not specified.  Assuming LATIN9\n");
00614       tmp = "LATIN9";
00615    }
00616 
00617    ast_free(encoding);
00618    if (!(encoding = ast_strdup(tmp))) {
00619       ast_config_destroy(cfg);
00620       ast_mutex_unlock(&pgsql_lock);
00621       return -1;
00622    }
00623 
00624    if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
00625       tmp = "";
00626    }
00627 
00628    ast_free(tz);
00629    tz = NULL;
00630 
00631    if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
00632       ast_config_destroy(cfg);
00633       ast_mutex_unlock(&pgsql_lock);
00634       return -1;
00635    }
00636 
00637    if (option_debug) {
00638       if (ast_strlen_zero(pghostname)) {
00639          ast_debug(1, "using default unix socket\n");
00640       } else {
00641          ast_debug(1, "got hostname of %s\n", pghostname);
00642       }
00643       ast_debug(1, "got port of %s\n", pgdbport);
00644       ast_debug(1, "got user of %s\n", pgdbuser);
00645       ast_debug(1, "got dbname of %s\n", pgdbname);
00646       ast_debug(1, "got password of %s\n", pgpassword);
00647       ast_debug(1, "got application name of %s\n", pgappname);
00648       ast_debug(1, "got sql table name of %s\n", table);
00649       ast_debug(1, "got encoding of %s\n", encoding);
00650       ast_debug(1, "got timezone of %s\n", tz);
00651    }
00652 
00653    pgsql_reconnect();
00654 
00655    if (PQstatus(conn) != CONNECTION_BAD) {
00656       char sqlcmd[768];
00657       char *fname, *ftype, *flen, *fnotnull, *fdef;
00658       int i, rows, version;
00659       ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00660       connected = 1;
00661       connect_time = time(NULL);
00662       records = 0;
00663       if (PQsetClientEncoding(conn, encoding)) {
00664 #ifdef HAVE_PGSQL_pg_encoding_to_char
00665          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00666 #else
00667          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00668 #endif
00669       }
00670       version = PQserverVersion(conn);
00671 
00672       if (version >= 70300) {
00673          char *schemaname, *tablename;
00674          if (strchr(table, '.')) {
00675             schemaname = ast_strdupa(table);
00676             tablename = strchr(schemaname, '.');
00677             *tablename++ = '\0';
00678          } else {
00679             schemaname = "";
00680             tablename = table;
00681          }
00682 
00683          /* Escape special characters in schemaname */
00684          if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00685             char *tmp = schemaname, *ptr;
00686 
00687             ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
00688             for (; *tmp; tmp++) {
00689                if (strchr("\\'", *tmp)) {
00690                   *ptr++ = *tmp;
00691                }
00692                *ptr++ = *tmp;
00693             }
00694             *ptr = '\0';
00695          }
00696          /* Escape special characters in tablename */
00697          if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00698             char *tmp = tablename, *ptr;
00699 
00700             ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
00701             for (; *tmp; tmp++) {
00702                if (strchr("\\'", *tmp)) {
00703                   *ptr++ = *tmp;
00704                }
00705                *ptr++ = *tmp;
00706             }
00707             *ptr = '\0';
00708          }
00709 
00710          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",
00711             tablename,
00712             ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00713       } else {
00714          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);
00715       }
00716       /* Query the columns */
00717       result = PQexec(conn, sqlcmd);
00718       if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00719          pgerror = PQresultErrorMessage(result);
00720          ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00721          PQclear(result);
00722          unload_module();
00723          ast_mutex_unlock(&pgsql_lock);
00724          return AST_MODULE_LOAD_DECLINE;
00725       }
00726 
00727       rows = PQntuples(result);
00728       if (rows == 0) {
00729          ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00730          PQclear(result);
00731          unload_module();
00732          ast_mutex_unlock(&pgsql_lock);
00733          return AST_MODULE_LOAD_DECLINE;
00734       }
00735 
00736       /* Clear out the columns list. */
00737       empty_columns();
00738 
00739       for (i = 0; i < rows; i++) {
00740          fname = PQgetvalue(result, i, 0);
00741          ftype = PQgetvalue(result, i, 1);
00742          flen = PQgetvalue(result, i, 2);
00743          fnotnull = PQgetvalue(result, i, 3);
00744          fdef = PQgetvalue(result, i, 4);
00745          if (atoi(flen) == -1) {
00746             /* For varchar columns, the maximum length is encoded in a different field */
00747             flen = PQgetvalue(result, i, 5);
00748          }
00749 
00750          cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00751          if (cur) {
00752             sscanf(flen, "%30d", &cur->len);
00753             cur->name = (char *)cur + sizeof(*cur);
00754             cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00755             strcpy(cur->name, fname);
00756             strcpy(cur->type, ftype);
00757             if (*fnotnull == 't') {
00758                cur->notnull = 1;
00759             } else {
00760                cur->notnull = 0;
00761             }
00762             if (!ast_strlen_zero(fdef)) {
00763                cur->hasdefault = 1;
00764             } else {
00765                cur->hasdefault = 0;
00766             }
00767             AST_RWLIST_WRLOCK(&psql_columns);
00768             AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00769             AST_RWLIST_UNLOCK(&psql_columns);
00770          }
00771       }
00772       PQclear(result);
00773    } else {
00774       pgerror = PQerrorMessage(conn);
00775       ast_log(LOG_ERROR, "Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
00776       ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00777       connected = 0;
00778       PQfinish(conn);
00779       conn = NULL;
00780    }
00781 
00782    ast_config_destroy(cfg);
00783 
00784    ast_mutex_unlock(&pgsql_lock);
00785    return 0;
00786 }

static void empty_columns ( void   )  [static]

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

00465 {
00466    struct columns *current;
00467    AST_RWLIST_WRLOCK(&psql_columns);
00468    while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00469       ast_free(current);
00470    }
00471    AST_RWLIST_UNLOCK(&psql_columns);
00472 
00473 }

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 133 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, NULL, status, and ast_cli_entry::usage.

00134 {
00135    switch (cmd) {
00136    case CLI_INIT:
00137       e->command = "cdr show pgsql status";
00138       e->usage =
00139          "Usage: cdr show pgsql status\n"
00140          "       Shows current connection status for cdr_pgsql\n";
00141       return NULL;
00142    case CLI_GENERATE:
00143       return NULL;
00144    }
00145 
00146    if (a->argc != 3)
00147       return CLI_SHOWUSAGE;
00148 
00149    if (connected) {
00150       char status[256], status2[100] = "";
00151       int ctime = time(NULL) - connect_time;
00152 
00153       if (pgdbport) {
00154          snprintf(status, 255, "Connected to %s@%s, port %s", pgdbname, pghostname, pgdbport);
00155       } else {
00156          snprintf(status, 255, "Connected to %s@%s", pgdbname, pghostname);
00157       }
00158 
00159       if (pgdbuser && *pgdbuser) {
00160          snprintf(status2, 99, " with username %s", pgdbuser);
00161       }
00162       if (table && *table) {
00163          snprintf(status2, 99, " using table %s", table);
00164       }
00165       if (ctime > 31536000) {
00166          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);
00167       } else if (ctime > 86400) {
00168          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);
00169       } else if (ctime > 3600) {
00170          ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00171       } else if (ctime > 60) {
00172          ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
00173       } else {
00174          ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
00175       }
00176       if (records == totalrecords) {
00177          ast_cli(a->fd, "  Wrote %d records since last restart.\n", totalrecords);
00178       } else {
00179          ast_cli(a->fd, "  Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
00180       }
00181    } else {
00182       ast_cli(a->fd, "Not currently connected to a PgSQL server.\n");
00183    }
00184    return CLI_SUCCESS;
00185 }

static int load_module ( void   )  [static]

Definition at line 788 of file cdr_pgsql.c.

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

00789 {
00790    ast_cli_register_multiple(cdr_pgsql_status_cli, sizeof(cdr_pgsql_status_cli) / sizeof(struct ast_cli_entry));
00791    if (config_module(0)) {
00792       return AST_MODULE_LOAD_DECLINE;
00793    }
00794    return ast_cdr_register(name, ast_module_info->description, pgsql_log)
00795       ? AST_MODULE_LOAD_DECLINE : 0;
00796 }

static int pgsql_log ( struct ast_cdr cdr  )  [static]

Definition at line 215 of file cdr_pgsql.c.

References ast_cdr::answer, ast_cdr_format_var(), 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(), buf, DATE_FORMAT, ast_cdr::end, columns::first, columns::hasdefault, LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::notnull, NULL, pgsql_lock, pgsql_reconnect(), result, ast_cdr::start, columns::type, and value.

Referenced by load_module(), and my_load_module().

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

static void pgsql_reconnect ( void   )  [static]

Definition at line 187 of file cdr_pgsql.c.

References ast_free, ast_log, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_strlen_zero, LOG_ERROR, and NULL.

Referenced by _pgsql_exec(), config_module(), destroy_pgsql(), parse_config(), pgsql_log(), process_my_load_module(), and store_pgsql().

00188 {
00189    struct ast_str *conn_info = ast_str_create(128);
00190    if (!conn_info) {
00191       ast_log(LOG_ERROR, "Failed to allocate memory for connection string.\n");
00192       return;
00193    }
00194 
00195    if (conn) {
00196       PQfinish(conn);
00197       conn = NULL;
00198    }
00199 
00200    ast_str_set(&conn_info, 0, "host=%s port=%s dbname=%s user=%s",
00201       pghostname, pgdbport, pgdbname, pgdbuser);
00202 
00203    if (!ast_strlen_zero(pgappname)) {
00204       ast_str_append(&conn_info, 0, " application_name=%s", pgappname);
00205    }
00206 
00207    if (!ast_strlen_zero(pgpassword)) {
00208       ast_str_append(&conn_info, 0, " password=%s", pgpassword);
00209    }
00210 
00211    conn = PQconnectdb(ast_str_buffer(conn_info));
00212    ast_free(conn_info);
00213 }

static int reload ( void   )  [static]

Definition at line 798 of file cdr_pgsql.c.

References config_module().

00799 {
00800    return config_module(1);
00801 }

static int unload_module ( void   )  [static]

Definition at line 475 of file cdr_pgsql.c.

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

00476 {
00477    if (ast_cdr_unregister(name)) {
00478       return -1;
00479    }
00480 
00481    ast_cli_unregister_multiple(cdr_pgsql_status_cli, ARRAY_LEN(cdr_pgsql_status_cli));
00482 
00483    if (conn) {
00484       PQfinish(conn);
00485       conn = NULL;
00486    }
00487    ast_free(pghostname);
00488    ast_free(pgdbname);
00489    ast_free(pgdbuser);
00490    ast_free(pgpassword);
00491    ast_free(pgappname);
00492    ast_free(pgdbport);
00493    ast_free(table);
00494    ast_free(encoding);
00495    ast_free(tz);
00496 
00497    empty_columns();
00498 
00499    return 0;
00500 }


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

Definition at line 809 of file cdr_pgsql.c.

Definition at line 809 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 84 of file cdr_pgsql.c.

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

Definition at line 65 of file cdr_pgsql.c.

PGconn* conn = NULL [static]

Definition at line 90 of file cdr_pgsql.c.

time_t connect_time = 0 [static]

Definition at line 79 of file cdr_pgsql.c.

int connected = 0 [static]

Definition at line 77 of file cdr_pgsql.c.

char* encoding [static]

Definition at line 74 of file cdr_pgsql.c.

Referenced by check_header(), and custom_prepare().

int maxsize = 512 [static]

Definition at line 78 of file cdr_pgsql.c.

int maxsize2 = 512 [static]

Definition at line 78 of file cdr_pgsql.c.

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

Definition at line 64 of file cdr_pgsql.c.

char* pgappname [static]

Definition at line 71 of file cdr_pgsql.c.

char* pgdbname [static]

Definition at line 68 of file cdr_pgsql.c.

char* pgdbport [static]

Definition at line 72 of file cdr_pgsql.c.

char* pgdbuser [static]

Definition at line 69 of file cdr_pgsql.c.

char* pghostname [static]

Definition at line 67 of file cdr_pgsql.c.

char* pgpassword [static]

Definition at line 70 of file cdr_pgsql.c.

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

int records [static]

Definition at line 81 of file cdr_pgsql.c.

char* table [static]

Definition at line 73 of file cdr_pgsql.c.

int totalrecords = 0 [static]

Definition at line 80 of file cdr_pgsql.c.

char* tz [static]


Generated on Thu Apr 16 06:29:25 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6