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 #include <asterisk.h>
00026
00027 #include <sys/types.h>
00028 #include <asterisk/config.h>
00029 #include <asterisk/options.h>
00030 #include <asterisk/channel.h>
00031 #include <asterisk/cdr.h>
00032 #include <asterisk/module.h>
00033 #include <asterisk/logger.h>
00034 #include <asterisk/cli.h>
00035
00036 #include <stdio.h>
00037 #include <string.h>
00038
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <time.h>
00042
00043 #include <mysql/mysql.h>
00044 #include <mysql/errmsg.h>
00045
00046 #include <sys/stat.h>
00047 #include <sys/types.h>
00048 #include <errno.h>
00049
00050 #define AST_MODULE "cdr_addon_mysql"
00051
00052 #define DATE_FORMAT "%Y-%m-%d %T"
00053
00054 static char *desc = "MySQL CDR Backend";
00055 static char *name = "mysql";
00056 static char *config = "cdr_mysql.conf";
00057 static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL;
00058 static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbtable_alloc = 0;
00059 static int dbport = 0;
00060 static int connected = 0;
00061 static time_t connect_time = 0;
00062 static int records = 0;
00063 static int totalrecords = 0;
00064 static int userfield = 0;
00065 static unsigned int timeout = 0;
00066
00067 AST_MUTEX_DEFINE_STATIC(mysql_lock);
00068
00069 static MYSQL mysql;
00070
00071 static char cdr_mysql_status_help[] =
00072 "Usage: cdr mysql status\n"
00073 " Shows current connection status for cdr_mysql\n";
00074
00075 static int handle_cdr_mysql_status(int fd, int argc, char *argv[])
00076 {
00077 if (connected) {
00078 char status[256], status2[100] = "";
00079 int ctime = time(NULL) - connect_time;
00080 if (dbport)
00081 snprintf(status, 255, "Connected to %s@%s, port %d", dbname, hostname, dbport);
00082 else if (dbsock)
00083 snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
00084 else
00085 snprintf(status, 255, "Connected to %s@%s", dbname, hostname);
00086
00087 if (dbuser && *dbuser)
00088 snprintf(status2, 99, " with username %s", dbuser);
00089 if (dbtable && *dbtable)
00090 snprintf(status2, 99, " using table %s", dbtable);
00091 if (ctime > 31536000) {
00092 ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00093 } else if (ctime > 86400) {
00094 ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00095 } else if (ctime > 3600) {
00096 ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00097 } else if (ctime > 60) {
00098 ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
00099 } else {
00100 ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
00101 }
00102 if (records == totalrecords)
00103 ast_cli(fd, " Wrote %d records since last restart.\n", totalrecords);
00104 else
00105 ast_cli(fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
00106 return RESULT_SUCCESS;
00107 } else {
00108 ast_cli(fd, "Not currently connected to a MySQL server.\n");
00109 return RESULT_FAILURE;
00110 }
00111 }
00112
00113 static struct ast_cli_entry cdr_mysql_status_cli =
00114 { { "cdr", "mysql", "status", NULL },
00115 handle_cdr_mysql_status, "Show connection status of cdr_mysql",
00116 cdr_mysql_status_help, NULL };
00117
00118 static int mysql_log(struct ast_cdr *cdr)
00119 {
00120 struct tm tm;
00121 struct timeval tv;
00122 struct ast_module_user *u;
00123 char *userfielddata = NULL;
00124 char sqlcmd[2048], timestr[128];
00125 char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
00126 int retries = 5;
00127 #ifdef MYSQL_LOGUNIQUEID
00128 char *uniqueid = NULL;
00129 #endif
00130
00131 ast_mutex_lock(&mysql_lock);
00132
00133 memset(sqlcmd, 0, 2048);
00134
00135 localtime_r(&cdr->start.tv_sec, &tm);
00136 strftime(timestr, 128, DATE_FORMAT, &tm);
00137
00138 db_reconnect:
00139 if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) {
00140
00141 mysql_init(&mysql);
00142
00143 if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
00144 ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00145 }
00146 if (mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
00147 connected = 1;
00148 connect_time = time(NULL);
00149 records = 0;
00150 } else {
00151 ast_log(LOG_ERROR, "cdr_mysql: cannot connect to database server %s.\n", hostname);
00152 connected = 0;
00153 }
00154 } else {
00155
00156 int error;
00157 if ((error = mysql_ping(&mysql))) {
00158 connected = 0;
00159 records = 0;
00160 switch (error) {
00161 case CR_SERVER_GONE_ERROR:
00162 case CR_SERVER_LOST:
00163 ast_log(LOG_ERROR, "cdr_mysql: Server has gone away. Attempting to reconnect.\n");
00164 break;
00165 default:
00166 ast_log(LOG_ERROR, "cdr_mysql: Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00167 }
00168 retries--;
00169 if (retries)
00170 goto db_reconnect;
00171 else
00172 ast_log(LOG_ERROR, "cdr_mysql: Retried to connect fives times, giving up.\n");
00173 }
00174 }
00175
00176
00177
00178
00179
00180
00181
00182
00183 if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
00184 mysql_escape_string(clid, cdr->clid, strlen(cdr->clid));
00185 if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
00186 mysql_escape_string(dcontext, cdr->dcontext, strlen(cdr->dcontext));
00187 if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
00188 mysql_escape_string(channel, cdr->channel, strlen(cdr->channel));
00189 if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
00190 mysql_escape_string(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
00191 if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
00192 mysql_escape_string(lastapp, cdr->lastapp, strlen(cdr->lastapp));
00193 if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
00194 mysql_escape_string(lastdata, cdr->lastdata, strlen(cdr->lastdata));
00195 #ifdef MYSQL_LOGUNIQUEID
00196 if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
00197 mysql_escape_string(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
00198 #endif
00199 if (userfield && ((userfielddata = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL))
00200 mysql_escape_string(userfielddata, cdr->userfield, strlen(cdr->userfield));
00201
00202
00203 #ifdef MYSQL_LOGUNIQUEID
00204 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) {
00205 #else
00206 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata)) {
00207 #endif
00208 ast_log(LOG_ERROR, "cdr_mysql: Out of memory error (insert fails)\n");
00209 ast_mutex_unlock(&mysql_lock);
00210 return -1;
00211 }
00212
00213 ast_log(LOG_DEBUG, "cdr_mysql: inserting a CDR record.\n");
00214
00215 if (userfield && userfielddata) {
00216 #ifdef MYSQL_LOGUNIQUEID
00217 sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid, userfielddata);
00218 #else
00219 sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, userfielddata);
00220 #endif
00221 } else {
00222 #ifdef MYSQL_LOGUNIQUEID
00223 sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid);
00224 #else
00225 sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode);
00226 #endif
00227 }
00228
00229 ast_log(LOG_DEBUG, "cdr_mysql: SQL command as follows: %s\n", sqlcmd);
00230
00231 if (connected) {
00232 if (mysql_real_query(&mysql, sqlcmd, strlen(sqlcmd))) {
00233 ast_log(LOG_ERROR, "mysql_cdr: Failed to insert into database: (%d) %s", mysql_errno(&mysql), mysql_error(&mysql));
00234 mysql_close(&mysql);
00235 connected = 0;
00236 } else {
00237 records++;
00238 totalrecords++;
00239 }
00240 }
00241 ast_mutex_unlock(&mysql_lock);
00242 return 0;
00243 }
00244
00245 static int my_unload_module(void)
00246 {
00247 ast_cli_unregister(&cdr_mysql_status_cli);
00248 if (connected) {
00249 mysql_close(&mysql);
00250 connected = 0;
00251 records = 0;
00252 }
00253 if (hostname && hostname_alloc) {
00254 free(hostname);
00255 hostname = NULL;
00256 hostname_alloc = 0;
00257 }
00258 if (dbname && dbname_alloc) {
00259 free(dbname);
00260 dbname = NULL;
00261 dbname_alloc = 0;
00262 }
00263 if (dbuser && dbuser_alloc) {
00264 free(dbuser);
00265 dbuser = NULL;
00266 dbuser_alloc = 0;
00267 }
00268 if (dbsock && dbsock_alloc) {
00269 free(dbsock);
00270 dbsock = NULL;
00271 dbsock_alloc = 0;
00272 }
00273 if (dbtable && dbtable_alloc) {
00274 free(dbtable);
00275 dbtable = NULL;
00276 dbtable_alloc = 0;
00277 }
00278 if (password && password_alloc) {
00279 free(password);
00280 password = NULL;
00281 password_alloc = 0;
00282 }
00283 dbport = 0;
00284 ast_cdr_unregister(name);
00285 return 0;
00286 }
00287
00288 static int my_load_module(void)
00289 {
00290 int res;
00291 struct ast_config *cfg;
00292 struct ast_variable *var;
00293 const char *tmp;
00294
00295 cfg = ast_config_load(config);
00296 if (!cfg) {
00297 ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
00298 return 0;
00299 }
00300
00301 var = ast_variable_browse(cfg, "global");
00302 if (!var) {
00303
00304 return 0;
00305 }
00306
00307 tmp = ast_variable_retrieve(cfg, "global", "hostname");
00308 if (tmp) {
00309 hostname = malloc(strlen(tmp) + 1);
00310 if (hostname != NULL) {
00311 hostname_alloc = 1;
00312 strcpy(hostname, tmp);
00313 } else {
00314 ast_log(LOG_ERROR, "Out of memory error.\n");
00315 return -1;
00316 }
00317 } else {
00318 ast_log(LOG_WARNING, "MySQL server hostname not specified. Assuming localhost\n");
00319 hostname = "localhost";
00320 }
00321
00322 tmp = ast_variable_retrieve(cfg, "global", "dbname");
00323 if (tmp) {
00324 dbname = malloc(strlen(tmp) + 1);
00325 if (dbname != NULL) {
00326 dbname_alloc = 1;
00327 strcpy(dbname, tmp);
00328 } else {
00329 ast_log(LOG_ERROR, "Out of memory error.\n");
00330 return -1;
00331 }
00332 } else {
00333 ast_log(LOG_WARNING, "MySQL database not specified. Assuming asteriskcdrdb\n");
00334 dbname = "asteriskcdrdb";
00335 }
00336
00337 tmp = ast_variable_retrieve(cfg, "global", "user");
00338 if (tmp) {
00339 dbuser = malloc(strlen(tmp) + 1);
00340 if (dbuser != NULL) {
00341 dbuser_alloc = 1;
00342 strcpy(dbuser, tmp);
00343 } else {
00344 ast_log(LOG_ERROR, "Out of memory error.\n");
00345 return -1;
00346 }
00347 } else {
00348 ast_log(LOG_WARNING, "MySQL database user not specified. Assuming root\n");
00349 dbuser = "root";
00350 }
00351
00352 tmp = ast_variable_retrieve(cfg, "global", "sock");
00353 if (tmp) {
00354 dbsock = malloc(strlen(tmp) + 1);
00355 if (dbsock != NULL) {
00356 dbsock_alloc = 1;
00357 strcpy(dbsock, tmp);
00358 } else {
00359 ast_log(LOG_ERROR, "Out of memory error.\n");
00360 return -1;
00361 }
00362 } else {
00363 ast_log(LOG_WARNING, "MySQL database sock file not specified. Using default\n");
00364 dbsock = NULL;
00365 }
00366
00367 tmp = ast_variable_retrieve(cfg, "global", "table");
00368 if (tmp) {
00369 dbtable = malloc(strlen(tmp) + 1);
00370 if (dbtable != NULL) {
00371 dbtable_alloc = 1;
00372 strcpy(dbtable, tmp);
00373 } else {
00374 ast_log(LOG_ERROR, "Out of memory error.\n");
00375 return -1;
00376 }
00377 } else {
00378 ast_log(LOG_NOTICE, "MySQL database table not specified. Assuming \"cdr\"\n");
00379 dbtable = "cdr";
00380 }
00381
00382 tmp = ast_variable_retrieve(cfg, "global", "password");
00383 if (tmp) {
00384 password = malloc(strlen(tmp) + 1);
00385 if (password != NULL) {
00386 password_alloc = 1;
00387 strcpy(password, tmp);
00388 } else {
00389 ast_log(LOG_ERROR, "Out of memory error.\n");
00390 return -1;
00391 }
00392 } else {
00393 ast_log(LOG_WARNING, "MySQL database password not specified. Assuming blank\n");
00394 password = "";
00395 }
00396
00397 tmp = ast_variable_retrieve(cfg, "global", "port");
00398 if (tmp) {
00399 if (sscanf(tmp, "%d", &dbport) < 1) {
00400 ast_log(LOG_WARNING, "Invalid MySQL port number. Using default\n");
00401 dbport = 0;
00402 }
00403 }
00404
00405 tmp = ast_variable_retrieve(cfg, "global", "timeout");
00406 if (tmp) {
00407 if (sscanf(tmp,"%d", &timeout) < 1) {
00408 ast_log(LOG_WARNING, "Invalid MySQL timeout number. Using default\n");
00409 timeout = 0;
00410 }
00411 }
00412
00413 tmp = ast_variable_retrieve(cfg, "global", "userfield");
00414 if (tmp) {
00415 if (sscanf(tmp, "%d", &userfield) < 1) {
00416 ast_log(LOG_WARNING, "Invalid MySQL configuration file\n");
00417 userfield = 0;
00418 }
00419 }
00420
00421 ast_config_destroy(cfg);
00422
00423 ast_log(LOG_DEBUG, "cdr_mysql: got hostname of %s\n", hostname);
00424 ast_log(LOG_DEBUG, "cdr_mysql: got port of %d\n", dbport);
00425 ast_log(LOG_DEBUG, "cdr_mysql: got a timeout of %d\n", timeout);
00426 if (dbsock)
00427 ast_log(LOG_DEBUG, "cdr_mysql: got sock file of %s\n", dbsock);
00428 ast_log(LOG_DEBUG, "cdr_mysql: got user of %s\n", dbuser);
00429 ast_log(LOG_DEBUG, "cdr_mysql: got dbname of %s\n", dbname);
00430 ast_log(LOG_DEBUG, "cdr_mysql: got password of %s\n", password);
00431
00432 mysql_init(&mysql);
00433
00434 if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
00435 ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00436 }
00437
00438 if (!mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
00439 ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", dbname, hostname);
00440 connected = 0;
00441 records = 0;
00442 } else {
00443 ast_log(LOG_DEBUG, "Successfully connected to MySQL database.\n");
00444 connected = 1;
00445 records = 0;
00446 connect_time = time(NULL);
00447 }
00448
00449 res = ast_cdr_register(name, desc, mysql_log);
00450 if (res) {
00451 ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n");
00452 } else {
00453 res = ast_cli_register(&cdr_mysql_status_cli);
00454 }
00455
00456 return res;
00457 }
00458
00459 static int load_module(void)
00460 {
00461 return my_load_module();
00462 }
00463
00464 static int unload_module(void)
00465 {
00466 return my_unload_module();
00467 }
00468
00469 static int reload(void)
00470 {
00471 int ret;
00472
00473 ast_mutex_lock(&mysql_lock);
00474 my_unload_module();
00475 ret = my_load_module();
00476 ast_mutex_unlock(&mysql_lock);
00477
00478 return ret;
00479 }
00480
00481 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MySQL CDR Backend");
00482