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: 11503 $")
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;
00068 PGresult *result;
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
00076 ast_mutex_lock(&pgsql_lock);
00077
00078 localtime_r(&cdr->start.tv_sec,&tm);
00079 strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
00080
00081 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00082 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00083 if (PQstatus(conn) != CONNECTION_BAD) {
00084 connected = 1;
00085 } else {
00086 pgerror = PQerrorMessage(conn);
00087 ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
00088 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00089 }
00090 }
00091
00092 if (connected) {
00093 char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
00094 char *uniqueid=NULL, *userfield=NULL;
00095
00096
00097 if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
00098 PQescapeString(clid, cdr->clid, strlen(cdr->clid));
00099 if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
00100 PQescapeString(dcontext, cdr->dcontext, strlen(cdr->dcontext));
00101 if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
00102 PQescapeString(channel, cdr->channel, strlen(cdr->channel));
00103 if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
00104 PQescapeString(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
00105 if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
00106 PQescapeString(lastapp, cdr->lastapp, strlen(cdr->lastapp));
00107 if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
00108 PQescapeString(lastdata, cdr->lastdata, strlen(cdr->lastdata));
00109 if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
00110 PQescapeString(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
00111 if ((userfield = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL)
00112 PQescapeString(userfield, cdr->userfield, strlen(cdr->userfield));
00113
00114
00115 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid) || (!userfield)) {
00116 ast_log(LOG_ERROR, "cdr_pgsql: Out of memory error (insert fails)\n");
00117 ast_mutex_unlock(&pgsql_lock);
00118 return -1;
00119 }
00120
00121 ast_log(LOG_DEBUG,"cdr_pgsql: inserting a CDR record.\n");
00122
00123 snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,"
00124 "lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES"
00125 " ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%ld,%ld,'%s',%ld,'%s','%s','%s')",
00126 table,timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,
00127 cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid, userfield);
00128
00129 ast_log(LOG_DEBUG,"cdr_pgsql: SQL command executed: %s\n",sqlcmd);
00130
00131
00132
00133
00134 if (PQstatus(conn) == CONNECTION_OK) {
00135 connected = 1;
00136 } else {
00137 ast_log(LOG_ERROR, "cdr_pgsql: Connection was lost... attempting to reconnect.\n");
00138 PQreset(conn);
00139 if (PQstatus(conn) == CONNECTION_OK) {
00140 ast_log(LOG_ERROR, "cdr_pgsql: Connection reestablished.\n");
00141 connected = 1;
00142 } else {
00143 pgerror = PQerrorMessage(conn);
00144 ast_log(LOG_ERROR, "cdr_pgsql: Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00145 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00146 connected = 0;
00147 ast_mutex_unlock(&pgsql_lock);
00148 return -1;
00149 }
00150 }
00151 result = PQexec(conn, sqlcmd);
00152 if ( PQresultStatus(result) != PGRES_COMMAND_OK) {
00153 pgerror = PQresultErrorMessage(result);
00154 ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database!\n");
00155 ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
00156 ast_log(LOG_ERROR,"cdr_pgsql: Connection may have been 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 result = PQexec(conn, sqlcmd);
00162 if ( PQresultStatus(result) != PGRES_COMMAND_OK)
00163 {
00164 pgerror = PQresultErrorMessage(result);
00165 ast_log(LOG_ERROR,"cdr_pgsql: HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
00166 ast_log(LOG_ERROR,"cdr_pgsql: Reason: %s\n", pgerror);
00167 }
00168 }
00169 ast_mutex_unlock(&pgsql_lock);
00170 return -1;
00171 }
00172 }
00173 ast_mutex_unlock(&pgsql_lock);
00174 return 0;
00175 }
00176
00177 char *description(void)
00178 {
00179 return desc;
00180 }
00181
00182 static int my_unload_module(void)
00183 {
00184 if (conn)
00185 PQfinish(conn);
00186 if (pghostname)
00187 free(pghostname);
00188 if (pgdbname)
00189 free(pgdbname);
00190 if (pgdbuser)
00191 free(pgdbuser);
00192 if (pgdbsock)
00193 free(pgdbsock);
00194 if (pgpassword)
00195 free(pgpassword);
00196 if (pgdbport)
00197 free(pgdbport);
00198 if (table)
00199 free(table);
00200 ast_cdr_unregister(name);
00201 return 0;
00202 }
00203
00204 static int process_my_load_module(struct ast_config *cfg)
00205 {
00206 int res;
00207 struct ast_variable *var;
00208 char *pgerror;
00209 char *tmp;
00210
00211 var = ast_variable_browse(cfg, "global");
00212 if (!var) {
00213
00214 return 0;
00215 }
00216
00217 tmp = ast_variable_retrieve(cfg,"global","hostname");
00218 if (tmp == NULL) {
00219 ast_log(LOG_WARNING,"PostgreSQL server hostname not specified. Assuming localhost\n");
00220 tmp = "localhost";
00221 }
00222 pghostname = strdup(tmp);
00223 if (pghostname == NULL) {
00224 ast_log(LOG_ERROR,"Out of memory error.\n");
00225 return -1;
00226 }
00227
00228 tmp = ast_variable_retrieve(cfg,"global","dbname");
00229 if (tmp == NULL) {
00230 ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asterisk\n");
00231 tmp = "asteriskcdrdb";
00232 }
00233 pgdbname = strdup(tmp);
00234 if (pgdbname == NULL) {
00235 ast_log(LOG_ERROR,"Out of memory error.\n");
00236 return -1;
00237 }
00238
00239 tmp = ast_variable_retrieve(cfg,"global","user");
00240 if (tmp == NULL) {
00241 ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming root\n");
00242 tmp = "root";
00243 }
00244 pgdbuser = strdup(tmp);
00245 if (pgdbuser == NULL) {
00246 ast_log(LOG_ERROR,"Out of memory error.\n");
00247 return -1;
00248 }
00249
00250 tmp = ast_variable_retrieve(cfg,"global","password");
00251 if (tmp == NULL) {
00252 ast_log(LOG_WARNING,"PostgreSQL database password not specified. Assuming blank\n");
00253 tmp = "";
00254 }
00255 pgpassword = strdup(tmp);
00256 if (pgpassword == NULL) {
00257 ast_log(LOG_ERROR,"Out of memory error.\n");
00258 return -1;
00259 }
00260
00261 tmp = ast_variable_retrieve(cfg,"global","port");
00262 if (tmp == NULL) {
00263 ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default 5432.\n");
00264 tmp = "5432";
00265 }
00266 pgdbport = strdup(tmp);
00267 if (pgdbport == NULL) {
00268 ast_log(LOG_ERROR,"Out of memory error.\n");
00269 return -1;
00270 }
00271
00272 tmp = ast_variable_retrieve(cfg,"global","table");
00273 if (tmp == NULL) {
00274 ast_log(LOG_WARNING,"CDR table not specified. Assuming cdr\n");
00275 tmp = "cdr";
00276 }
00277 table = strdup(tmp);
00278 if (table == NULL) {
00279 ast_log(LOG_ERROR,"Out of memory error.\n");
00280 return -1;
00281 }
00282
00283 ast_log(LOG_DEBUG,"cdr_pgsql: got hostname of %s\n",pghostname);
00284 ast_log(LOG_DEBUG,"cdr_pgsql: got port of %s\n",pgdbport);
00285 if (pgdbsock)
00286 ast_log(LOG_DEBUG,"cdr_pgsql: got sock file of %s\n",pgdbsock);
00287 ast_log(LOG_DEBUG,"cdr_pgsql: got user of %s\n",pgdbuser);
00288 ast_log(LOG_DEBUG,"cdr_pgsql: got dbname of %s\n",pgdbname);
00289 ast_log(LOG_DEBUG,"cdr_pgsql: got password of %s\n",pgpassword);
00290 ast_log(LOG_DEBUG,"cdr_pgsql: got sql table name of %s\n",table);
00291
00292 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00293 if (PQstatus(conn) != CONNECTION_BAD) {
00294 ast_log(LOG_DEBUG,"Successfully connected to PostgreSQL database.\n");
00295 connected = 1;
00296 } else {
00297 pgerror = PQerrorMessage(conn);
00298 ast_log(LOG_ERROR, "cdr_pgsql: Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00299 ast_log(LOG_ERROR, "cdr_pgsql: Reason: %s\n", pgerror);
00300 connected = 0;
00301 }
00302
00303 res = ast_cdr_register(name, desc, pgsql_log);
00304 if (res) {
00305 ast_log(LOG_ERROR, "Unable to register PGSQL CDR handling\n");
00306 }
00307 return res;
00308 }
00309
00310 static int my_load_module(void)
00311 {
00312 struct ast_config *cfg;
00313 int res;
00314 cfg = ast_config_load(config);
00315 if (!cfg) {
00316 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00317 return 0;
00318 }
00319 res = process_my_load_module(cfg);
00320 ast_config_destroy(cfg);
00321 return res;
00322 }
00323
00324 int load_module(void)
00325 {
00326 return my_load_module();
00327 }
00328
00329 int unload_module(void)
00330 {
00331 return my_unload_module();
00332 }
00333
00334 int reload(void)
00335 {
00336 my_unload_module();
00337 return my_load_module();
00338 }
00339
00340 int usecount(void)
00341 {
00342
00343 if ( ast_mutex_trylock(&pgsql_lock) ) {
00344 return 1;
00345 } else {
00346 ast_mutex_unlock(&pgsql_lock);
00347 return 0;
00348 }
00349 }
00350
00351 char *key()
00352 {
00353 return ASTERISK_GPL_KEY;
00354 }