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