Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:48:25 2009

## Asterisk developer's documentation

# cdr_pgsql.c File Reference

PostgreSQL CDR logger. More...

`#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.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"`

Include dependency graph for cdr_pgsql.c:

Go to the source code of this file.

## Defines

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

## Functions

| AST_MUTEX_DEFINE_STATIC (pgsql_lock)
char * | description (void)
| Provides a description of the module.
char * | key ()
| Returns the ASTERISK_GPL_KEY.
int | load_module (void)
| Initialize the module.
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)
int | reload (void)
| Reload stuff.
int | unload_module (void)
| Cleanup all module structures, sockets, etc.
int | usecount (void)
| Provides a usecount.

## Variables

static char * | config = "cdr_pgsql.conf"
PGconn * | conn = NULL
static int | connected = 0
static char * | desc = "PostgreSQL CDR Backend"
static char * | name = "pgsql"
static char * | pgdbname = NULL
static char * | pgdbport = NULL
static char * | pgdbsock = NULL
static char * | pgdbuser = NULL
static char * | pghostname = NULL
static char * | pgpassword = NULL
PGresult * | result = NULL
static char * | table = NULL

## Detailed Description

PostgreSQL CDR logger.

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

See also

- CDR configuration
- http://www.postgresql.org/

Definition in file cdr_pgsql.c.

## Define Documentation

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

Definition at line 57 of file cdr_pgsql.c.

## Function Documentation

AST_MUTEX_DEFINE_STATIC | ( | pgsql_lock |  | ) |

char* description | ( | void |  | ) |

Provides a description of the module.

Returns:
  a short description of your module

Definition at line 182 of file cdr_pgsql.c.

    00183 {
    00184    return desc;
    00185 }

char* key | ( | void |  | ) |

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

     char *key(void) {
             return ASTERISK_GPL_KEY;
     }

Returns:
  ASTERISK_GPL_KEY

Definition at line 356 of file cdr_pgsql.c.

References ASTERISK_GPL_KEY.

    00357 {
    00358    return ASTERISK_GPL_KEY;
    00359 }

int load_module | ( | void |  | ) |

Initialize the module.

This function is called at module load time. Put all code in here that needs to set up your module's hardware, software, registrations, etc.

Returns:
  This function should return 0 on success and non-zero on failure. If the module is not loaded successfully, Asterisk will call its unload_module() function.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
  int Always 0.

TE STUFF END

Definition at line 329 of file cdr_pgsql.c.

References my_load_module().

    00330 {
    00331    return my_load_module();
    00332 }

static int my_load_module | ( | void |  | ) | `[static]`

Definition at line 315 of file cdr_pgsql.c.

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

Referenced by load_module(), and reload().

    00316 {
    00317    struct ast_config *cfg;
    00318    int res;
    00319    cfg = ast_config_load(config);
    00320    if (!cfg) {
    00321       ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
    00322       return 0;
    00323    }
    00324    res = process_my_load_module(cfg);
    00325    ast_config_destroy(cfg);
    00326    return res;
    00327 }

static int my_unload_module | ( | void |  | ) | `[static]`

Definition at line 187 of file cdr_pgsql.c.

References ast_cdr_unregister(), and free.

Referenced by reload(), and unload_module().

    00188 {
    00189    if (conn)
    00190       PQfinish(conn);
    00191    if (pghostname)
    00192       free(pghostname);
    00193    if (pgdbname)
    00194       free(pgdbname);
    00195    if (pgdbuser)
    00196       free(pgdbuser);
    00197    if (pgdbsock)
    00198       free(pgdbsock);
    00199    if (pgpassword)
    00200       free(pgpassword);
    00201    if (pgdbport)
    00202       free(pgdbport);
    00203    if (table)
    00204       free(table);
    00205    ast_cdr_unregister(name);
    00206    return 0;
    00207 }

static int pgsql_log | ( | struct ast_cdr * | cdr | ) | `[static]`

Definition at line 70 of file cdr_pgsql.c.

References ast_cdr::accountcode, ast_cdr::amaflags, ast_cdr_disp2str(), 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, ast_cdr::src, ast_cdr::start, ast_cdr::uniqueid, and ast_cdr::userfield.

Referenced by process_my_load_module().

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

static int process_my_load_module | ( | struct ast_config * | cfg | ) | `[static]`

Definition at line 209 of file cdr_pgsql.c.

References ast_cdr_register(), ast_log(), ast_variable_browse(), ast_variable_retrieve(), LOG_DEBUG, LOG_ERROR, LOG_WARNING, pgsql_log(), strdup, and var.

Referenced by my_load_module().

    00210 {
    00211    int res;
    00212    struct ast_variable *var;
    00213         char *pgerror;
    00214    char *tmp;
    00215
    00216    var = ast_variable_browse(cfg, "global");
    00217    if (!var) {
    00218       /* nothing configured */
    00219       return 0;
    00220    }
    00221
    00222    tmp = ast_variable_retrieve(cfg,"global","hostname");
    00223    if (tmp == NULL) {
    00224       ast_log(LOG_WARNING,"PostgreSQL server hostname not specified.  Assuming localhost\n");
    00225       tmp = "localhost";
    00226    }
    00227    pghostname = strdup(tmp);
    00228    if (pghostname == NULL) {
    00229       ast_log(LOG_ERROR,"Out of memory error.\n");
    00230       return -1;
    00231    }
    00232
    00233    tmp = ast_variable_retrieve(cfg,"global","dbname");
    00234    if (tmp == NULL) {
    00235       ast_log(LOG_WARNING,"PostgreSQL database not specified.  Assuming asterisk\n");
    00236       tmp = "asteriskcdrdb";
    00237    }
    00238    pgdbname = strdup(tmp);
    00239    if (pgdbname == NULL) {
    00240       ast_log(LOG_ERROR,"Out of memory error.\n");
    00241       return -1;
    00242    }
    00243
    00244    tmp = ast_variable_retrieve(cfg,"global","user");
    00245    if (tmp == NULL) {
    00246       ast_log(LOG_WARNING,"PostgreSQL database user not specified.  Assuming root\n");
    00247       tmp = "root";
    00248    }
    00249    pgdbuser = strdup(tmp);
    00250    if (pgdbuser == NULL) {
    00251       ast_log(LOG_ERROR,"Out of memory error.\n");
    00252       return -1;
    00253    }
    00254
    00255    tmp = ast_variable_retrieve(cfg,"global","password");
    00256    if (tmp == NULL) {
    00257       ast_log(LOG_WARNING,"PostgreSQL database password not specified.  Assuming blank\n");
    00258       tmp = "";
    00259    }
    00260    pgpassword = strdup(tmp);
    00261    if (pgpassword == NULL) {
    00262       ast_log(LOG_ERROR,"Out of memory error.\n");
    00263       return -1;
    00264    }
    00265
    00266    tmp = ast_variable_retrieve(cfg,"global","port");
    00267    if (tmp == NULL) {
    00268       ast_log(LOG_WARNING,"PostgreSQL database port not specified.  Using default 5432.\n");
    00269       tmp = "5432";
    00270    }
    00271    pgdbport = strdup(tmp);
    00272    if (pgdbport == NULL) {
    00273       ast_log(LOG_ERROR,"Out of memory error.\n");
    00274       return -1;
    00275    }
    00276
    00277    tmp = ast_variable_retrieve(cfg,"global","table");
    00278    if (tmp == NULL) {
    00279       ast_log(LOG_WARNING,"CDR table not specified.  Assuming cdr\n");
    00280       tmp = "cdr";
    00281    }
    00282    table = strdup(tmp);
    00283    if (table == NULL) {
    00284       ast_log(LOG_ERROR,"Out of memory error.\n");
    00285       return -1;
    00286    }
    00287
    00288    ast_log(LOG_DEBUG,"cdr_pgsql: got hostname of %s\n",pghostname);
    00289    ast_log(LOG_DEBUG,"cdr_pgsql: got port of %s\n",pgdbport);
    00290    if (pgdbsock)
    00291       ast_log(LOG_DEBUG,"cdr_pgsql: got sock file of %s\n",pgdbsock);
    00292    ast_log(LOG_DEBUG,"cdr_pgsql: got user of %s\n",pgdbuser);
    00293    ast_log(LOG_DEBUG,"cdr_pgsql: got dbname of %s\n",pgdbname);
    00294    ast_log(LOG_DEBUG,"cdr_pgsql: got password of %s\n",pgpassword);
    00295    ast_log(LOG_DEBUG,"cdr_pgsql: got sql table name of %s\n",table);
    00296
    00297    conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
    00298    if (PQstatus(conn) != CONNECTION_BAD) {
    00299       ast_log(LOG_DEBUG,"Successfully connected to PostgreSQL database.\n");
    00300       connected = 1;
    00301    } else {
    00302                 pgerror = PQerrorMessage(conn);
    00303       ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
    00304                 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
    00305       connected = 0;
    00306    }
    00307
    00308    res = ast_cdr_register(name, desc, pgsql_log);
    00309    if (res) {
    00310       ast_log(LOG_ERROR, "Unable to register PGSQL CDR handling\n");
    00311    }
    00312    return res;
    00313 }

int reload | ( | void |  | ) |

Reload stuff.

This function is where any reload routines take place. Re-read config files, change signalling, whatever is appropriate on a reload.

Returns:
  The return value is not used.

Definition at line 339 of file cdr_pgsql.c.

References my_load_module(), and my_unload_module().

    00340 {
    00341    my_unload_module();
    00342    return my_load_module();
    00343 }

int unload_module | ( | void |  | ) |

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
  Zero on success, or non-zero on error.

Definition at line 334 of file cdr_pgsql.c.

References my_unload_module().

    00335 {
    00336    return my_unload_module();
    00337 }

int usecount | ( | void |  | ) |

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
  The module's usecount.

Definition at line 345 of file cdr_pgsql.c.

References ast_mutex_trylock(), and ast_mutex_unlock().

    00346 {
    00347    /* To be able to unload the module */
    00348    if ( ast_mutex_trylock(&pgsql_lock) ) {
    00349       return 1;
    00350    } else {
    00351       ast_mutex_unlock(&pgsql_lock);
    00352       return 0;
    00353    }
    00354 }

## Variable Documentation

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

Definition at line 61 of file cdr_pgsql.c.

PGconn* conn = NULL

Definition at line 67 of file cdr_pgsql.c.

int connected = 0`[static]`

Definition at line 63 of file cdr_pgsql.c.

char* desc = "PostgreSQL CDR Backend"`[static]`

Definition at line 59 of file cdr_pgsql.c.

char* name = "pgsql"`[static]`

Definition at line 60 of file cdr_pgsql.c.

char * pgdbname = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.

char * pgdbport = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.

char * pgdbsock = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.

char * pgdbuser = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.

char* pghostname = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.

char * pgpassword = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.

PGresult* result = NULL

Definition at line 68 of file cdr_pgsql.c.

Referenced by aopen_read(), ast_channel_spy_read_frame(), ast_config_internal_load(), ast_config_load(), ast_privacy_check(), ast_waitfordigit(), bestdata_read(), check_sip_domain(), complete_sip_peer(), complete_sip_user(), cut_exec(), detzcode(), dial_exec_full(), get_member_status(), i4l_read(), i4l_write(), mgcpsock_read(), ogg_vorbis_open(), PGSQL_exec(), read_samples(), sort_exec(), and tmcomp().

char * table = NULL`[static]`

Definition at line 62 of file cdr_pgsql.c.