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