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 #include <sys/types.h>
00036 #include <stdio.h>
00037 #include <string.h>
00038
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <time.h>
00042
00043 #include <libpq-fe.h>
00044
00045 #include "asterisk.h"
00046
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 90170 $")
00048
00049 #include "asterisk/config.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/cdr.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/logger.h"
00055 #include "asterisk.h"
00056
00057 #define DATE_FORMAT "%Y-%m-%d %T"
00058
00059 static char *desc = "PostgreSQL CDR Backend";
00060 static char *name = "pgsql";
00061 static char *config = "cdr_pgsql.conf";
00062 static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbsock = NULL, *pgdbport = NULL, *table = NULL;
00063 static int connected = 0;
00064
00065 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00066
00067 PGconn *conn = NULL;
00068 PGresult *result = NULL;
00069
00070 static int pgsql_log(struct ast_cdr *cdr)
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
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
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
00137
00138
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 }
00181
00182 char *description(void)
00183 {
00184 return desc;
00185 }
00186
00187 static int my_unload_module(void)
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 }
00208
00209 static int process_my_load_module(struct ast_config *cfg)
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
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 }
00314
00315 static int my_load_module(void)
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 }
00328
00329 int load_module(void)
00330 {
00331 return my_load_module();
00332 }
00333
00334 int unload_module(void)
00335 {
00336 return my_unload_module();
00337 }
00338
00339 int reload(void)
00340 {
00341 my_unload_module();
00342 return my_load_module();
00343 }
00344
00345 int usecount(void)
00346 {
00347
00348 if ( ast_mutex_trylock(&pgsql_lock) ) {
00349 return 1;
00350 } else {
00351 ast_mutex_unlock(&pgsql_lock);
00352 return 0;
00353 }
00354 }
00355
00356 char *key()
00357 {
00358 return ASTERISK_GPL_KEY;
00359 }