00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include "asterisk.h"
00040
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00042
00043 #include <sys/types.h>
00044 #include <stdio.h>
00045 #include <string.h>
00046 #include <stdlib.h>
00047 #include <unistd.h>
00048 #include <time.h>
00049
00050 #include <libpq-fe.h>
00051
00052 #include "asterisk/config.h"
00053 #include "asterisk/options.h"
00054 #include "asterisk/channel.h"
00055 #include "asterisk/cdr.h"
00056 #include "asterisk/module.h"
00057 #include "asterisk/logger.h"
00058 #include "asterisk.h"
00059
00060 #define DATE_FORMAT "%Y-%m-%d %T"
00061
00062 static char *name = "pgsql";
00063 static char *config = "cdr_pgsql.conf";
00064 static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL;
00065 static int connected = 0;
00066
00067 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00068
00069 static PGconn *conn = NULL;
00070 static PGresult *result = NULL;
00071
00072 static int pgsql_log(struct ast_cdr *cdr)
00073 {
00074 struct tm tm;
00075 static char timestr[128];
00076 static char sqlcmd[2048];
00077 char *pgerror;
00078
00079 ast_mutex_lock(&pgsql_lock);
00080
00081 localtime_r(&cdr->start.tv_sec,&tm);
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 } else {
00089 pgerror = PQerrorMessage(conn);
00090 ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
00091 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00092 }
00093 }
00094
00095 if (connected) {
00096 char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
00097 char *uniqueid=NULL, *userfield=NULL;
00098
00099
00100 if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
00101 PQescapeString(clid, cdr->clid, strlen(cdr->clid));
00102 if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
00103 PQescapeString(dcontext, cdr->dcontext, strlen(cdr->dcontext));
00104 if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
00105 PQescapeString(channel, cdr->channel, strlen(cdr->channel));
00106 if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
00107 PQescapeString(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
00108 if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
00109 PQescapeString(lastapp, cdr->lastapp, strlen(cdr->lastapp));
00110 if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
00111 PQescapeString(lastdata, cdr->lastdata, strlen(cdr->lastdata));
00112 if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
00113 PQescapeString(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
00114 if ((userfield = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL)
00115 PQescapeString(userfield, cdr->userfield, strlen(cdr->userfield));
00116
00117
00118 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid) || (!userfield)) {
00119 ast_log(LOG_ERROR, "cdr_pgsql: Out of memory error (insert fails)\n");
00120 ast_mutex_unlock(&pgsql_lock);
00121 return -1;
00122 }
00123
00124 if (option_debug > 1)
00125 ast_log(LOG_DEBUG, "cdr_pgsql: inserting a CDR record.\n");
00126
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,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,
00132 cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfield);
00133
00134 if (option_debug > 2)
00135 ast_log(LOG_DEBUG, "cdr_pgsql: SQL command executed: %s\n",sqlcmd);
00136
00137
00138
00139
00140 if (PQstatus(conn) == CONNECTION_OK) {
00141 connected = 1;
00142 } else {
00143 ast_log(LOG_ERROR, "cdr_pgsql: Connection was lost... attempting to reconnect.\n");
00144 PQreset(conn);
00145 if (PQstatus(conn) == CONNECTION_OK) {
00146 ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
00147 connected = 1;
00148 } else {
00149 pgerror = PQerrorMessage(conn);
00150 ast_log(LOG_ERROR, "cdr_pgsql: Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00151 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00152 connected = 0;
00153 ast_mutex_unlock(&pgsql_lock);
00154 return -1;
00155 }
00156 }
00157 result = PQexec(conn, sqlcmd);
00158 if ( PQresultStatus(result) != PGRES_COMMAND_OK) {
00159 pgerror = PQresultErrorMessage(result);
00160 ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database!\n");
00161 ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
00162 ast_log(LOG_ERROR,"cdr_pgsql: Connection may have been lost... attempting to reconnect.\n");
00163 PQreset(conn);
00164 if (PQstatus(conn) == CONNECTION_OK) {
00165 ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
00166 connected = 1;
00167 result = PQexec(conn, sqlcmd);
00168 if ( PQresultStatus(result) != PGRES_COMMAND_OK)
00169 {
00170 pgerror = PQresultErrorMessage(result);
00171 ast_log(LOG_ERROR,"cdr_pgsql: HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
00172 ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
00173 }
00174 }
00175 ast_mutex_unlock(&pgsql_lock);
00176 return -1;
00177 }
00178 }
00179 ast_mutex_unlock(&pgsql_lock);
00180 return 0;
00181 }
00182
00183 static int my_unload_module(void)
00184 {
00185 if (conn)
00186 PQfinish(conn);
00187 if (pghostname)
00188 free(pghostname);
00189 if (pgdbname)
00190 free(pgdbname);
00191 if (pgdbuser)
00192 free(pgdbuser);
00193 if (pgpassword)
00194 free(pgpassword);
00195 if (pgdbport)
00196 free(pgdbport);
00197 if (table)
00198 free(table);
00199 ast_cdr_unregister(name);
00200 return 0;
00201 }
00202
00203 static int process_my_load_module(struct ast_config *cfg)
00204 {
00205 struct ast_variable *var;
00206 char *pgerror;
00207 const char *tmp;
00208
00209 if (!(var = ast_variable_browse(cfg, "global")))
00210 return 0;
00211
00212 if (!(tmp = ast_variable_retrieve(cfg,"global","hostname"))) {
00213 ast_log(LOG_WARNING,"PostgreSQL server hostname not specified. Assuming unix socket connection\n");
00214 tmp = "";
00215 }
00216
00217 if (!(pghostname = ast_strdup(tmp)))
00218 return -1;
00219
00220 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00221 ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asterisk\n");
00222 tmp = "asterisk";
00223 }
00224
00225 if (!(pgdbname = ast_strdup(tmp)))
00226 return -1;
00227
00228 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00229 ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming asterisk\n");
00230 tmp = "asterisk";
00231 }
00232
00233 if (!(pgdbuser = ast_strdup(tmp)))
00234 return -1;
00235
00236 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00237 ast_log(LOG_WARNING,"PostgreSQL database password not specified. Assuming blank\n");
00238 tmp = "";
00239 }
00240
00241 if (!(pgpassword = ast_strdup(tmp)))
00242 return -1;
00243
00244 if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
00245 ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default 5432.\n");
00246 tmp = "5432";
00247 }
00248
00249 if (!(pgdbport = ast_strdup(tmp)))
00250 return -1;
00251
00252 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00253 ast_log(LOG_WARNING,"CDR table not specified. Assuming cdr\n");
00254 tmp = "cdr";
00255 }
00256
00257 if (!(table = ast_strdup(tmp)))
00258 return -1;
00259
00260 if (option_debug) {
00261 if (ast_strlen_zero(pghostname))
00262 ast_log(LOG_DEBUG, "cdr_pgsql: using default unix socket\n");
00263 else
00264 ast_log(LOG_DEBUG, "cdr_pgsql: got hostname of %s\n", pghostname);
00265 ast_log(LOG_DEBUG, "cdr_pgsql: got port of %s\n", pgdbport);
00266 ast_log(LOG_DEBUG, "cdr_pgsql: got user of %s\n", pgdbuser);
00267 ast_log(LOG_DEBUG, "cdr_pgsql: got dbname of %s\n", pgdbname);
00268 ast_log(LOG_DEBUG, "cdr_pgsql: got password of %s\n", pgpassword);
00269 ast_log(LOG_DEBUG, "cdr_pgsql: got sql table name of %s\n", table);
00270 }
00271
00272 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00273 if (PQstatus(conn) != CONNECTION_BAD) {
00274 if (option_debug)
00275 ast_log(LOG_DEBUG, "Successfully connected to PostgreSQL database.\n");
00276 connected = 1;
00277 } else {
00278 pgerror = PQerrorMessage(conn);
00279 ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00280 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00281 connected = 0;
00282 }
00283
00284 return ast_cdr_register(name, ast_module_info->description, pgsql_log);
00285 }
00286
00287 static int my_load_module(void)
00288 {
00289 struct ast_config *cfg;
00290 int res;
00291
00292 if (!(cfg = ast_config_load(config))) {
00293 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00294 return AST_MODULE_LOAD_DECLINE;
00295 }
00296
00297 res = process_my_load_module(cfg);
00298 ast_config_destroy(cfg);
00299
00300 return res;
00301 }
00302
00303 static int load_module(void)
00304 {
00305 return my_load_module();
00306 }
00307
00308 static int unload_module(void)
00309 {
00310 return my_unload_module();
00311 }
00312
00313 static int reload(void)
00314 {
00315 my_unload_module();
00316 return my_load_module();
00317 }
00318
00319 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CDR Backend",
00320 .load = load_module,
00321 .unload = unload_module,
00322 .reload = reload,
00323 );