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