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