minor simplification of a conditional statement
[asterisk/asterisk.git] / cdr / cdr_pgsql.c
old mode 100755 (executable)
new mode 100644 (file)
index 15a5f0e..4ccd4aa
@@ -1,7 +1,7 @@
 /*
- * Asterisk -- A telephony toolkit for Linux.
+ * Asterisk -- An open source telephony toolkit.
  *
- * PostgreSQL CDR logger 
+ * Copyright (C) 2003 - 2006
  *
  * Matthew D. Hardeman <mhardemn@papersoft.com> 
  * Adapted from the MySQL CDR logger originally by James Sharp 
@@ -9,53 +9,77 @@
  * Modified September 2003
  * Matthew D. Hardeman <mhardemn@papersoft.com>
  *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
  * This program is free software, distributed under the terms of
- * the GNU General Public License.
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
  *
+ * \brief PostgreSQL CDR logger 
+ * 
+ * \author Matthew D. Hardeman <mhardemn@papersoft.com> 
+ * \extref PostgreSQL http://www.postgresql.org/
+ *
+ * See also
+ * \arg \ref Config_cdr
+ * \arg http://www.postgresql.org/
+ * \ingroup cdr_drivers
  */
 
-#include <sys/types.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 "../asterisk.h"
+/*** MODULEINFO
+       <depend>pgsql</depend>
+ ***/
 
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#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"
+#include "asterisk.h"
+
 #define DATE_FORMAT "%Y-%m-%d %T"
 
-static char *desc = "PostgreSQL CDR Backend";
 static char *name = "pgsql";
 static char *config = "cdr_pgsql.conf";
-static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbsock = NULL, *pgdbport = NULL;
-static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbport_alloc = 0;
+static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL;
 static int connected = 0;
 
 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
 
-PGconn         *conn;
-PGresult       *result;
+static PGconn  *conn = NULL;
 
 static int pgsql_log(struct ast_cdr *cdr)
 {
-       struct tm tm;
+       struct ast_tm tm;
        char sqlcmd[2048] = "", timestr[128];
        char *pgerror;
+       PGresult *result;
 
        ast_mutex_lock(&pgsql_lock);
 
-       localtime_r(&cdr->start.tv_sec,&tm);
-       strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
+       ast_localtime(&cdr->start, &tm, NULL);
+       ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
 
        if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
                conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
@@ -64,7 +88,9 @@ static int pgsql_log(struct ast_cdr *cdr)
                } else {
                        pgerror = PQerrorMessage(conn);
                        ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s.  Calls will not be logged!\n", pghostname);
-                        ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
+                       ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
+                       PQfinish(conn);
+                       conn = NULL;
                }
        }
 
@@ -97,11 +123,16 @@ static int pgsql_log(struct ast_cdr *cdr)
                        return -1;
                }
 
-               ast_log(LOG_DEBUG,"cdr_pgsql: inserting a CDR record.\n");
+               ast_debug(2, "cdr_pgsql: inserting a CDR record.\n");
 
-               snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO cdr (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s','%s')",timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfield);
-               ast_log(LOG_DEBUG,"cdr_pgsql: SQL command executed:  %s\n",sqlcmd);
-       
+               snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,"
+                                "lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES"
+                                " ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%ld,%ld,'%s',%ld,'%s','%s','%s')",
+                                table,timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,
+                                cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfield);
+               
+               ast_debug(3, "cdr_pgsql: SQL command executed:  %s\n",sqlcmd);
+               
                /* Test to be sure we're still connected... */
                /* If we're connected, and connection is working, good. */
                /* Otherwise, attempt reconnect.  If it fails... sorry... */
@@ -117,217 +148,181 @@ static int pgsql_log(struct ast_cdr *cdr)
                                pgerror = PQerrorMessage(conn);
                                ast_log(LOG_ERROR, "cdr_pgsql: Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
                                ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
+                               PQfinish(conn);
+                               conn = NULL;
                                connected = 0;
                                ast_mutex_unlock(&pgsql_lock);
                                return -1;
                        }
                }
                result = PQexec(conn, sqlcmd);
-               if ( PQresultStatus(result) != PGRES_COMMAND_OK) {
-                        pgerror = PQresultErrorMessage(result);
+               if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+                       pgerror = PQresultErrorMessage(result);
                        ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database!\n");
-                        ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
+                       ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
+                       ast_log(LOG_ERROR,"cdr_pgsql: Connection may have been lost... attempting to reconnect.\n");
+                       PQreset(conn);
+                       if (PQstatus(conn) == CONNECTION_OK) {
+                               ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
+                               connected = 1;
+                               PQclear(result);
+                               result = PQexec(conn, sqlcmd);
+                               if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+                                       pgerror = PQresultErrorMessage(result);
+                                       ast_log(LOG_ERROR,"cdr_pgsql: HARD ERROR!  Attempted reconnection failed.  DROPPING CALL RECORD!\n");
+                                       ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
+                               }
+                       }
                        ast_mutex_unlock(&pgsql_lock);
+                       PQclear(result);
                        return -1;
                }
+               PQclear(result);
        }
        ast_mutex_unlock(&pgsql_lock);
        return 0;
 }
 
-char *description(void)
-{
-       return desc;
-}
-
 static int my_unload_module(void)
 { 
        PQfinish(conn);
-       connected = 0;
-       if (pghostname && hostname_alloc) {
-               free(pghostname);
-               pghostname = NULL;
-               hostname_alloc = 0;
-       }
-       if (pgdbname && dbname_alloc) {
-               free(pgdbname);
-               pgdbname = NULL;
-               dbname_alloc = 0;
-       }
-       if (pgdbuser && dbuser_alloc) {
-               free(pgdbuser);
-               pgdbuser = NULL;
-               dbuser_alloc = 0;
-       }
-       if (pgdbsock && dbsock_alloc) {
-               free(pgdbsock);
-               pgdbsock = NULL;
-               dbsock_alloc = 0;
-       }
-       if (pgpassword && password_alloc) {
-               free(pgpassword);
-               pgpassword = NULL;
-               password_alloc = 0;
-       }
-       if (pgdbport && dbport_alloc) {
-               free(pgdbport);
-               pgdbport = NULL;
-               dbport_alloc = 0;
-       }
+       if (pghostname)
+               ast_free(pghostname);
+       if (pgdbname)
+               ast_free(pgdbname);
+       if (pgdbuser)
+               ast_free(pgdbuser);
+       if (pgpassword)
+               ast_free(pgpassword);
+       if (pgdbport)
+               ast_free(pgdbport);
+       if (table)
+               ast_free(table);
        ast_cdr_unregister(name);
        return 0;
 }
 
-static int my_load_module(void)
+static int process_my_load_module(struct ast_config *cfg)
 {
-       int res;
-       struct ast_config *cfg;
        struct ast_variable *var;
         char *pgerror;
-       char *tmp;
+       const char *tmp;
 
-       cfg = ast_load(config);
-       if (!cfg) {
-               ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
-               return 0;
-       }
-       
-       var = ast_variable_browse(cfg, "global");
-       if (!var) {
-               /* nothing configured */
+       if (!(var = ast_variable_browse(cfg, "global")))
                return 0;
-       }
 
-       tmp = ast_variable_retrieve(cfg,"global","hostname");
-       if (tmp) {
-               pghostname = malloc(strlen(tmp) + 1);
-               if (pghostname != NULL) {
-                       memset(pghostname, 0, strlen(tmp) + 1);
-                       hostname_alloc = 1;
-                       strncpy(pghostname, tmp, strlen(tmp));
-               } else {
-                       ast_log(LOG_ERROR,"Out of memory error.\n");
-                       return -1;
-               }
-       } else {
-               ast_log(LOG_WARNING,"PostgreSQL server hostname not specified.  Assuming localhost\n");
-               pghostname = "localhost";
+       if (!(tmp = ast_variable_retrieve(cfg,"global","hostname"))) {
+               ast_log(LOG_WARNING,"PostgreSQL server hostname not specified.  Assuming unix socket connection\n");
+               tmp = "";       /* connect via UNIX-socket by default */
        }
+       
+       if (!(pghostname = ast_strdup(tmp)))
+               return -1;
 
-       tmp = ast_variable_retrieve(cfg,"global","dbname");
-       if (tmp) {
-               pgdbname = malloc(strlen(tmp) + 1);
-               if (pgdbname != NULL) {
-                       memset(pgdbname, 0, strlen(tmp) + 1);
-                       dbname_alloc = 1;
-                       strncpy(pgdbname, tmp, strlen(tmp));
-               } else {
-                       ast_log(LOG_ERROR,"Out of memory error.\n");
-                       return -1;
-               }
-       } else {
+       if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
                ast_log(LOG_WARNING,"PostgreSQL database not specified.  Assuming asterisk\n");
-               pgdbname = "asteriskcdrdb";
+               tmp = "asteriskcdrdb";
        }
 
-       tmp = ast_variable_retrieve(cfg,"global","user");
-       if (tmp) {
-               pgdbuser = malloc(strlen(tmp) + 1);
-               if (pgdbuser != NULL) {
-                       memset(pgdbuser, 0, strlen(tmp) + 1);
-                       dbuser_alloc = 1;
-                       strncpy(pgdbuser, tmp, strlen(tmp));
-               } else {
-                       ast_log(LOG_ERROR,"Out of memory error.\n");
-                       return -1;
-               }
-       } else {
-               ast_log(LOG_WARNING,"PostgreSQL database user not specified.  Assuming root\n");
-               pgdbuser = "root";
+       if (!(pgdbname = ast_strdup(tmp)))
+               return -1;
+
+       if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
+               ast_log(LOG_WARNING,"PostgreSQL database user not specified.  Assuming asterisk\n");
+               tmp = "asterisk";
        }
 
-       tmp = ast_variable_retrieve(cfg,"global","password");
-       if (tmp) {
-               pgpassword = malloc(strlen(tmp) + 1);
-               if (pgpassword != NULL) {
-                       memset(pgpassword, 0, strlen(tmp) + 1);
-                       password_alloc = 1;
-                       strncpy(pgpassword, tmp, strlen(tmp));
-               } else {
-                       ast_log(LOG_ERROR,"Out of memory error.\n");
-                       return -1;
-               }
-       } else {
+       if (!(pgdbuser = ast_strdup(tmp)))
+               return -1;
+
+       if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
                ast_log(LOG_WARNING,"PostgreSQL database password not specified.  Assuming blank\n");
-               pgpassword = "";
+               tmp = "";
        }
 
-       tmp = ast_variable_retrieve(cfg,"global","port");
-       if (tmp) {
-               pgdbport = malloc(strlen(tmp) + 1);
-               if (pgdbport != NULL) {
-                       memset(pgdbport, 0, strlen(tmp) + 1);
-                       dbport_alloc = 1;
-                       strncpy(pgdbport, tmp, strlen(tmp));
-               } else {
-                       ast_log(LOG_ERROR,"Out of memory error.\n");
-                       return -1;
-               }
-       } else {
+       if (!(pgpassword = ast_strdup(tmp)))
+               return -1;
+
+       if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
                ast_log(LOG_WARNING,"PostgreSQL database port not specified.  Using default 5432.\n");
-               pgdbport = "5432";
+               tmp = "5432";
        }
 
-       ast_destroy(cfg);
+       if (!(pgdbport = ast_strdup(tmp)))
+               return -1;
 
-       ast_log(LOG_DEBUG,"cdr_pgsql: got hostname of %s\n",pghostname);
-       ast_log(LOG_DEBUG,"cdr_pgsql: got port of %s\n",pgdbport);
-       if (pgdbsock)
-               ast_log(LOG_DEBUG,"cdr_pgsql: got sock file of %s\n",pgdbsock);
-       ast_log(LOG_DEBUG,"cdr_pgsql: got user of %s\n",pgdbuser);
-       ast_log(LOG_DEBUG,"cdr_pgsql: got dbname of %s\n",pgdbname);
-       ast_log(LOG_DEBUG,"cdr_pgsql: got password of %s\n",pgpassword);
+       if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
+               ast_log(LOG_WARNING,"CDR table not specified.  Assuming cdr\n");
+               tmp = "cdr";
+       }
 
+       if (!(table = ast_strdup(tmp)))
+               return -1;
+
+       if (option_debug) {
+               if (ast_strlen_zero(pghostname))
+                       ast_debug(1, "cdr_pgsql: using default unix socket\n");
+               else
+                       ast_debug(1, "cdr_pgsql: got hostname of %s\n", pghostname);
+                       ast_debug(1, "cdr_pgsql: got port of %s\n", pgdbport);
+                       ast_debug(1, "cdr_pgsql: got user of %s\n", pgdbuser);
+                       ast_debug(1, "cdr_pgsql: got dbname of %s\n", pgdbname);
+                       ast_debug(1, "cdr_pgsql: got password of %s\n", pgpassword);
+                       ast_debug(1, "cdr_pgsql: got sql table name of %s\n", table);
+       }
+       
        conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
        if (PQstatus(conn) != CONNECTION_BAD) {
-               ast_log(LOG_DEBUG,"Successfully connected to PostgreSQL database.\n");
+               ast_debug(1, "Successfully connected to PostgreSQL database.\n");
                connected = 1;
        } else {
                 pgerror = PQerrorMessage(conn);
-               ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s.  Calls will not be logged!\n", pghostname);
+               ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
                 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
                connected = 0;
        }
 
-       res = ast_cdr_register(name, desc, pgsql_log);
-       if (res) {
-               ast_log(LOG_ERROR, "Unable to register PGSQL CDR handling\n");
+       return ast_cdr_register(name, ast_module_info->description, pgsql_log);
+}
+
+static int my_load_module(void)
+{
+       struct ast_config *cfg;
+       int res;
+
+       if (!(cfg = ast_config_load(config))) {
+               ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
+               return AST_MODULE_LOAD_DECLINE;
        }
+
+       res = process_my_load_module(cfg);
+       ast_config_destroy(cfg);
+
        return res;
 }
 
-int load_module(void)
+static int load_module(void)
 {
        return my_load_module();
 }
 
-int unload_module(void)
+static int unload_module(void)
 {
        return my_unload_module();
 }
 
-int reload(void)
+static int reload(void)
 {
+       int res;
+       ast_mutex_lock(&pgsql_lock);
        my_unload_module();
-       return my_load_module();
-}
-
-int usecount(void)
-{
-       return connected;
+       res = my_load_module();
+       ast_mutex_unlock(&pgsql_lock);
+       return res;
 }
 
-char *key()
-{
-       return ASTERISK_GPL_KEY;
-}
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CDR Backend",
+               .load = load_module,
+               .unload = unload_module,
+               .reload = reload,
+              );