Sat Aug 6 00:39:40 2011

Asterisk developer's documentation


cdr_pgsql.c File Reference

PostgreSQL CDR logger. More...

#include "asterisk.h"
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <libpq-fe.h>
#include "asterisk/config.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"

Go to the source code of this file.

Defines

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

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int load_module (void)
static int my_load_module (void)
static int my_unload_module (void)
static int pgsql_log (struct ast_cdr *cdr)
static int process_my_load_module (struct ast_config *cfg)
static int reload (void)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, }
static const struct ast_module_infoast_module_info = &__mod_info
static char * config = "cdr_pgsql.conf"
static PGconn * conn = NULL
static int connected = 0
static char * encoding = NULL
static 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 = ((ast_mutex_t) PTHREAD_MUTEX_INITIALIZER )
static char * table = NULL


Detailed Description

PostgreSQL CDR logger.

Author:
Matthew D. Hardeman <mhardemn@papersoft.com>
See also

Definition in file cdr_pgsql.c.


Define Documentation

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

Definition at line 60 of file cdr_pgsql.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 361 of file cdr_pgsql.c.

static void __unreg_module ( void   )  [static]

Definition at line 361 of file cdr_pgsql.c.

static int load_module ( void   )  [static]

Definition at line 337 of file cdr_pgsql.c.

References my_load_module().

00338 {
00339    return my_load_module();
00340 }

static int my_load_module ( void   )  [static]

Definition at line 321 of file cdr_pgsql.c.

References ast_config_destroy(), ast_config_load(), ast_log(), AST_MODULE_LOAD_DECLINE, LOG_WARNING, and process_my_load_module().

Referenced by load_module(), and reload().

00322 {
00323    struct ast_config *cfg;
00324    int res;
00325 
00326    if (!(cfg = ast_config_load(config))) {
00327       ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00328       return AST_MODULE_LOAD_DECLINE;
00329    }
00330 
00331    res = process_my_load_module(cfg);
00332    ast_config_destroy(cfg);
00333 
00334    return res;
00335 }

static int my_unload_module ( void   )  [static]

Definition at line 200 of file cdr_pgsql.c.

References ast_cdr_unregister(), and free.

Referenced by reload(), and unload_module().

00201 { 
00202    PQfinish(conn);
00203    if (pghostname)
00204       free(pghostname);
00205    if (pgdbname)
00206       free(pgdbname);
00207    if (pgdbuser)
00208       free(pgdbuser);
00209    if (pgpassword)
00210       free(pgpassword);
00211    if (pgdbport)
00212       free(pgdbport);
00213    if (table)
00214       free(table);
00215    if (encoding) {
00216       free(encoding);
00217    }
00218    ast_cdr_unregister(name);
00219    return 0;
00220 }

static int pgsql_log ( struct ast_cdr cdr  )  [static]

Definition at line 71 of file cdr_pgsql.c.

References ast_cdr::accountcode, ast_cdr::amaflags, ast_cdr_disp2str(), ast_localtime(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_cdr::billsec, ast_cdr::channel, ast_cdr::clid, DATE_FORMAT, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_cdr::duration, ast_cdr::lastapp, ast_cdr::lastdata, LOG_DEBUG, LOG_ERROR, LOG_WARNING, option_debug, pgsql_lock, ast_cdr::src, ast_cdr::start, t, ast_cdr::uniqueid, and ast_cdr::userfield.

Referenced by process_my_load_module().

00072 {
00073    struct tm tm;
00074    time_t t = cdr->start.tv_sec;
00075    char sqlcmd[2048] = "", timestr[128];
00076    char *pgerror;
00077    PGresult *result;
00078 
00079    ast_mutex_lock(&pgsql_lock);
00080 
00081    ast_localtime(&t, &tm, NULL);
00082    strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
00083 
00084    if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00085       conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00086       if (PQstatus(conn) != CONNECTION_BAD) {
00087          connected = 1;
00088          if (PQsetClientEncoding(conn, encoding)) {
00089 #ifdef HAVE_PGSQL_pg_encoding_to_char
00090             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00091 #else
00092             ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00093 #endif
00094          }
00095       } else {
00096          pgerror = PQerrorMessage(conn);
00097          ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s.  Calls will not be logged!\n", pghostname);
00098          ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00099          PQfinish(conn);
00100          conn = NULL;
00101       }
00102    }
00103 
00104    if (connected) {
00105       char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
00106       char *src=NULL, *dst=NULL, *uniqueid=NULL, *userfield=NULL;
00107       int pgerr;
00108 
00109       /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */
00110       if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
00111          PQescapeStringConn(conn, clid, cdr->clid, strlen(cdr->clid), &pgerr);
00112       if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
00113          PQescapeStringConn(conn, dcontext, cdr->dcontext, strlen(cdr->dcontext), &pgerr);
00114       if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
00115          PQescapeStringConn(conn, channel, cdr->channel, strlen(cdr->channel), &pgerr);
00116       if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
00117          PQescapeStringConn(conn, dstchannel, cdr->dstchannel, strlen(cdr->dstchannel), &pgerr);
00118       if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
00119          PQescapeStringConn(conn, lastapp, cdr->lastapp, strlen(cdr->lastapp), &pgerr);
00120       if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
00121          PQescapeStringConn(conn, lastdata, cdr->lastdata, strlen(cdr->lastdata), &pgerr);
00122       if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
00123          PQescapeStringConn(conn, uniqueid, cdr->uniqueid, strlen(cdr->uniqueid), &pgerr);
00124       if ((userfield = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL)
00125          PQescapeStringConn(conn, userfield, cdr->userfield, strlen(cdr->userfield), &pgerr);
00126       if ((src = alloca(strlen(cdr->src) * 2 + 1)) != NULL)
00127          PQescapeStringConn(conn, src, cdr->src, strlen(cdr->src), &pgerr);
00128       if ((dst = alloca(strlen(cdr->dst) * 2 + 1)) != NULL)
00129          PQescapeStringConn(conn, dst, cdr->dst, strlen(cdr->dst), &pgerr);
00130 
00131       /* Check for all alloca failures above at once */
00132       if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid) || (!userfield) || (!src) || (!dst)) {
00133          ast_log(LOG_ERROR, "cdr_pgsql:  Out of memory error (insert fails)\n");
00134          ast_mutex_unlock(&pgsql_lock);
00135          return -1;
00136       }
00137 
00138       if (option_debug > 1)
00139          ast_log(LOG_DEBUG, "cdr_pgsql: inserting a CDR record.\n");
00140 
00141       snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,"
00142              "lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES"
00143              " ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%ld,%ld,'%s',%ld,'%s','%s','%s')",
00144              table, timestr, clid, src, dst, dcontext, channel, dstchannel, lastapp, lastdata,
00145              cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfield);
00146       
00147       if (option_debug > 2)
00148          ast_log(LOG_DEBUG, "cdr_pgsql: SQL command executed:  %s\n",sqlcmd);
00149       
00150       /* Test to be sure we're still connected... */
00151       /* If we're connected, and connection is working, good. */
00152       /* Otherwise, attempt reconnect.  If it fails... sorry... */
00153       if (PQstatus(conn) == CONNECTION_OK) {
00154          connected = 1;
00155       } else {
00156          ast_log(LOG_ERROR, "cdr_pgsql: Connection was lost... attempting to reconnect.\n");
00157          PQreset(conn);
00158          if (PQstatus(conn) == CONNECTION_OK) {
00159             ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
00160             connected = 1;
00161          } else {
00162             pgerror = PQerrorMessage(conn);
00163             ast_log(LOG_ERROR, "cdr_pgsql: Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00164             ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00165             PQfinish(conn);
00166             conn = NULL;
00167             connected = 0;
00168             ast_mutex_unlock(&pgsql_lock);
00169             return -1;
00170          }
00171       }
00172       result = PQexec(conn, sqlcmd);
00173       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00174          pgerror = PQresultErrorMessage(result);
00175          ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database!\n");
00176          ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
00177          ast_log(LOG_ERROR,"cdr_pgsql: Connection may have been lost... attempting to reconnect.\n");
00178          PQreset(conn);
00179          if (PQstatus(conn) == CONNECTION_OK) {
00180             ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
00181             connected = 1;
00182             PQclear(result);
00183             result = PQexec(conn, sqlcmd);
00184             if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00185                pgerror = PQresultErrorMessage(result);
00186                ast_log(LOG_ERROR,"cdr_pgsql: HARD ERROR!  Attempted reconnection failed.  DROPPING CALL RECORD!\n");
00187                ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
00188             }
00189          }
00190          ast_mutex_unlock(&pgsql_lock);
00191          PQclear(result);
00192          return -1;
00193       }
00194       PQclear(result);
00195    }
00196    ast_mutex_unlock(&pgsql_lock);
00197    return 0;
00198 }

static int process_my_load_module ( struct ast_config cfg  )  [static]

Definition at line 222 of file cdr_pgsql.c.

References ast_cdr_register(), ast_log(), ast_strdup, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), LOG_DEBUG, LOG_ERROR, LOG_WARNING, option_debug, pgsql_log(), and var.

Referenced by my_load_module().

00223 {
00224    struct ast_variable *var;
00225         char *pgerror;
00226    const char *tmp;
00227 
00228    if (!(var = ast_variable_browse(cfg, "global")))
00229       return 0;
00230 
00231    if (!(tmp = ast_variable_retrieve(cfg,"global","hostname"))) {
00232       ast_log(LOG_WARNING,"PostgreSQL server hostname not specified.  Assuming unix socket connection\n");
00233       tmp = "";   /* connect via UNIX-socket by default */
00234    }
00235    
00236    if (!(pghostname = ast_strdup(tmp)))
00237       return -1;
00238 
00239    if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00240       ast_log(LOG_WARNING,"PostgreSQL database not specified.  Assuming asterisk\n");
00241       tmp = "asteriskcdrdb";
00242    }
00243 
00244    if (!(pgdbname = ast_strdup(tmp)))
00245       return -1;
00246 
00247    if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00248       ast_log(LOG_WARNING,"PostgreSQL database user not specified.  Assuming asterisk\n");
00249       tmp = "asterisk";
00250    }
00251 
00252    if (!(pgdbuser = ast_strdup(tmp)))
00253       return -1;
00254 
00255    if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00256       ast_log(LOG_WARNING,"PostgreSQL database password not specified.  Assuming blank\n");
00257       tmp = "";
00258    }
00259 
00260    if (!(pgpassword = ast_strdup(tmp)))
00261       return -1;
00262 
00263    if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
00264       ast_log(LOG_WARNING,"PostgreSQL database port not specified.  Using default 5432.\n");
00265       tmp = "5432";
00266    }
00267 
00268    if (!(pgdbport = ast_strdup(tmp)))
00269       return -1;
00270 
00271    if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00272       ast_log(LOG_WARNING,"CDR table not specified.  Assuming cdr\n");
00273       tmp = "cdr";
00274    }
00275 
00276    if (!(table = ast_strdup(tmp)))
00277       return -1;
00278 
00279    if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00280       tmp = "LATIN9";
00281    }
00282 
00283    if (!(encoding = ast_strdup(tmp))) {
00284       return -1;
00285    }
00286 
00287    if (option_debug) {
00288       if (ast_strlen_zero(pghostname))
00289          ast_log(LOG_DEBUG, "cdr_pgsql: using default unix socket\n");
00290       else
00291          ast_log(LOG_DEBUG, "cdr_pgsql: got hostname of %s\n", pghostname);
00292       ast_log(LOG_DEBUG, "cdr_pgsql: got port of %s\n", pgdbport);
00293       ast_log(LOG_DEBUG, "cdr_pgsql: got user of %s\n", pgdbuser);
00294       ast_log(LOG_DEBUG, "cdr_pgsql: got dbname of %s\n", pgdbname);
00295       ast_log(LOG_DEBUG, "cdr_pgsql: got password of %s\n", pgpassword);
00296       ast_log(LOG_DEBUG, "cdr_pgsql: got sql table name of %s\n", table);
00297    }
00298 
00299    conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00300    if (PQstatus(conn) != CONNECTION_BAD) {
00301       if (option_debug)
00302          ast_log(LOG_DEBUG, "Successfully connected to PostgreSQL database.\n");
00303       connected = 1;
00304       if (PQsetClientEncoding(conn, encoding)) {
00305 #ifdef HAVE_PGSQL_pg_encoding_to_char
00306          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00307 #else
00308          ast_log(LOG_WARNING, "Failed to set encoding to '%s'.  Encoding set to default.\n", encoding);
00309 #endif
00310       }
00311    } else {
00312       pgerror = PQerrorMessage(conn);
00313       ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
00314                 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00315       connected = 0;
00316    }
00317 
00318    return ast_cdr_register(name, ast_module_info->description, pgsql_log);
00319 }

static int reload ( void   )  [static]

Definition at line 347 of file cdr_pgsql.c.

References ast_mutex_lock(), ast_mutex_unlock(), my_load_module(), my_unload_module(), and pgsql_lock.

00348 {
00349    int res;
00350    ast_mutex_lock(&pgsql_lock);
00351    my_unload_module();
00352    res = my_load_module();
00353    ast_mutex_unlock(&pgsql_lock);
00354    return res;
00355 }

static int unload_module ( void   )  [static]

Definition at line 342 of file cdr_pgsql.c.

References my_unload_module().

00343 {
00344    return my_unload_module();
00345 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 361 of file cdr_pgsql.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 361 of file cdr_pgsql.c.

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

Definition at line 63 of file cdr_pgsql.c.

PGconn* conn = NULL [static]

Definition at line 69 of file cdr_pgsql.c.

int connected = 0 [static]

Definition at line 65 of file cdr_pgsql.c.

char * encoding = NULL [static]

Definition at line 64 of file cdr_pgsql.c.

Referenced by check_header(), and custom_prepare().

char* name = "pgsql" [static]

Definition at line 62 of file cdr_pgsql.c.

char * pgdbname = NULL [static]

Definition at line 64 of file cdr_pgsql.c.

char * pgdbport = NULL [static]

Definition at line 64 of file cdr_pgsql.c.

char * pgdbuser = NULL [static]

Definition at line 64 of file cdr_pgsql.c.

char* pghostname = NULL [static]

Definition at line 64 of file cdr_pgsql.c.

char * pgpassword = NULL [static]

Definition at line 64 of file cdr_pgsql.c.

ast_mutex_t pgsql_lock = ((ast_mutex_t) PTHREAD_MUTEX_INITIALIZER ) [static]

Definition at line 67 of file cdr_pgsql.c.

Referenced by config_pgsql(), load_module(), pgsql_log(), realtime_multi_pgsql(), realtime_pgsql(), reload(), unload_module(), and update_pgsql().

char * table = NULL [static]

Definition at line 64 of file cdr_pgsql.c.


Generated on Sat Aug 6 00:39:40 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7