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, *src=NULL, *dst=NULL, *accountcode=NULL;
00126 int retries = 5;
00127 #ifdef MYSQL_LOGUNIQUEID
00128 char *uniqueid = NULL;
00129 #endif
00130 #if MYSQL_VERSION_ID >= 50013
00131 my_bool my_bool_true = 1;
00132 #endif
00133
00134 ast_mutex_lock(&mysql_lock);
00135
00136 memset(sqlcmd, 0, 2048);
00137
00138 ast_localtime(&cdr->start.tv_sec, &tm, NULL);
00139 strftime(timestr, 128, DATE_FORMAT, &tm);
00140
00141 db_reconnect:
00142 if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) {
00143
00144 mysql_init(&mysql);
00145
00146 if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
00147 ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00148 }
00149 #if MYSQL_VERSION_ID >= 50013
00150
00151 if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
00152 ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00153 }
00154 #endif
00155 if (mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
00156 connected = 1;
00157 connect_time = time(NULL);
00158 records = 0;
00159 } else {
00160 ast_log(LOG_ERROR, "cdr_mysql: cannot connect to database server %s.\n", hostname);
00161 connected = 0;
00162 }
00163 } else {
00164
00165 int error;
00166 if ((error = mysql_ping(&mysql))) {
00167 connected = 0;
00168 records = 0;
00169 switch (mysql_errno(&mysql)) {
00170 case CR_SERVER_GONE_ERROR:
00171 case CR_SERVER_LOST:
00172 ast_log(LOG_ERROR, "cdr_mysql: Server has gone away. Attempting to reconnect.\n");
00173 break;
00174 default:
00175 ast_log(LOG_ERROR, "cdr_mysql: Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00176 }
00177 retries--;
00178 if (retries)
00179 goto db_reconnect;
00180 else
00181 ast_log(LOG_ERROR, "cdr_mysql: Retried to connect fives times, giving up.\n");
00182 }
00183 }
00184
00185
00186
00187
00188
00189
00190
00191
00192 if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
00193 mysql_escape_string(clid, cdr->clid, strlen(cdr->clid));
00194 if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
00195 mysql_escape_string(dcontext, cdr->dcontext, strlen(cdr->dcontext));
00196 if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
00197 mysql_escape_string(channel, cdr->channel, strlen(cdr->channel));
00198 if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
00199 mysql_escape_string(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
00200 if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
00201 mysql_escape_string(lastapp, cdr->lastapp, strlen(cdr->lastapp));
00202 if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
00203 mysql_escape_string(lastdata, cdr->lastdata, strlen(cdr->lastdata));
00204 if ((src = alloca(strlen(cdr->src) * 2 + 1)) != NULL)
00205 mysql_escape_string(src, cdr->src, strlen(cdr->src));
00206 if ((dst = alloca(strlen(cdr->dst) * 2 + 1)) != NULL)
00207 mysql_escape_string(dst, cdr->dst, strlen(cdr->dst));
00208 if ((accountcode = alloca(strlen(cdr->accountcode) * 2 + 1)) != NULL)
00209 mysql_escape_string(accountcode, cdr->accountcode, strlen(cdr->accountcode));
00210 #ifdef MYSQL_LOGUNIQUEID
00211 if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
00212 mysql_escape_string(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
00213 #endif
00214 if (userfield && ((userfielddata = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL))
00215 mysql_escape_string(userfielddata, cdr->userfield, strlen(cdr->userfield));
00216
00217
00218 #ifdef MYSQL_LOGUNIQUEID
00219 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid) || !(src) || (!dst) || (!accountcode)) {
00220 #else
00221 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || !(src) || (!dst) || (!accountcode)) {
00222 #endif
00223 ast_log(LOG_ERROR, "cdr_mysql: Out of memory error (insert fails)\n");
00224 ast_mutex_unlock(&mysql_lock);
00225 return -1;
00226 }
00227
00228 if (option_debug)
00229 ast_log(LOG_DEBUG, "cdr_mysql: inserting a CDR record.\n");
00230
00231 if (userfield && userfielddata) {
00232 #ifdef MYSQL_LOGUNIQUEID
00233 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, src, dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, accountcode, uniqueid, userfielddata);
00234 #else
00235 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, src, dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, accountcode, userfielddata);
00236 #endif
00237 } else {
00238 #ifdef MYSQL_LOGUNIQUEID
00239 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, src, dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, accountcode, uniqueid);
00240 #else
00241 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, src, dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, accountcode);
00242 #endif
00243 }
00244
00245 if (option_debug)
00246 ast_log(LOG_DEBUG, "cdr_mysql: SQL command as follows: %s\n", sqlcmd);
00247
00248 if (connected) {
00249 if (mysql_real_query(&mysql, sqlcmd, strlen(sqlcmd))) {
00250 ast_log(LOG_ERROR, "mysql_cdr: Failed to insert into database: (%d) %s", mysql_errno(&mysql), mysql_error(&mysql));
00251 mysql_close(&mysql);
00252 connected = 0;
00253 } else {
00254 records++;
00255 totalrecords++;
00256 }
00257 }
00258 ast_mutex_unlock(&mysql_lock);
00259 return 0;
00260 }
00261
00262 static int my_unload_module(void)
00263 {
00264 ast_cli_unregister(&cdr_mysql_status_cli);
00265 if (connected) {
00266 mysql_close(&mysql);
00267 connected = 0;
00268 records = 0;
00269 }
00270 if (hostname && hostname_alloc) {
00271 free(hostname);
00272 hostname = NULL;
00273 hostname_alloc = 0;
00274 }
00275 if (dbname && dbname_alloc) {
00276 free(dbname);
00277 dbname = NULL;
00278 dbname_alloc = 0;
00279 }
00280 if (dbuser && dbuser_alloc) {
00281 free(dbuser);
00282 dbuser = NULL;
00283 dbuser_alloc = 0;
00284 }
00285 if (dbsock && dbsock_alloc) {
00286 free(dbsock);
00287 dbsock = NULL;
00288 dbsock_alloc = 0;
00289 }
00290 if (dbtable && dbtable_alloc) {
00291 free(dbtable);
00292 dbtable = NULL;
00293 dbtable_alloc = 0;
00294 }
00295 if (password && password_alloc) {
00296 free(password);
00297 password = NULL;
00298 password_alloc = 0;
00299 }
00300 dbport = 0;
00301 ast_cdr_unregister(name);
00302 return 0;
00303 }
00304
00305 static int my_load_module(void)
00306 {
00307 int res;
00308 struct ast_config *cfg;
00309 struct ast_variable *var;
00310 const char *tmp;
00311 #if MYSQL_VERSION_ID >= 50013
00312 my_bool my_bool_true = 1;
00313 #endif
00314
00315 cfg = ast_config_load(config);
00316 if (!cfg) {
00317 ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
00318 return 0;
00319 }
00320
00321 var = ast_variable_browse(cfg, "global");
00322 if (!var) {
00323
00324 return 0;
00325 }
00326
00327 tmp = ast_variable_retrieve(cfg, "global", "hostname");
00328 if (tmp) {
00329 hostname = malloc(strlen(tmp) + 1);
00330 if (hostname != NULL) {
00331 hostname_alloc = 1;
00332 strcpy(hostname, tmp);
00333 } else {
00334 ast_log(LOG_ERROR, "Out of memory error.\n");
00335 return -1;
00336 }
00337 } else {
00338 ast_log(LOG_WARNING, "MySQL server hostname not specified. Assuming localhost\n");
00339 hostname = "localhost";
00340 }
00341
00342 tmp = ast_variable_retrieve(cfg, "global", "dbname");
00343 if (tmp) {
00344 dbname = malloc(strlen(tmp) + 1);
00345 if (dbname != NULL) {
00346 dbname_alloc = 1;
00347 strcpy(dbname, tmp);
00348 } else {
00349 ast_log(LOG_ERROR, "Out of memory error.\n");
00350 return -1;
00351 }
00352 } else {
00353 ast_log(LOG_WARNING, "MySQL database not specified. Assuming asteriskcdrdb\n");
00354 dbname = "asteriskcdrdb";
00355 }
00356
00357 tmp = ast_variable_retrieve(cfg, "global", "user");
00358 if (tmp) {
00359 dbuser = malloc(strlen(tmp) + 1);
00360 if (dbuser != NULL) {
00361 dbuser_alloc = 1;
00362 strcpy(dbuser, tmp);
00363 } else {
00364 ast_log(LOG_ERROR, "Out of memory error.\n");
00365 return -1;
00366 }
00367 } else {
00368 ast_log(LOG_WARNING, "MySQL database user not specified. Assuming root\n");
00369 dbuser = "root";
00370 }
00371
00372 tmp = ast_variable_retrieve(cfg, "global", "sock");
00373 if (tmp) {
00374 dbsock = malloc(strlen(tmp) + 1);
00375 if (dbsock != NULL) {
00376 dbsock_alloc = 1;
00377 strcpy(dbsock, tmp);
00378 } else {
00379 ast_log(LOG_ERROR, "Out of memory error.\n");
00380 return -1;
00381 }
00382 } else {
00383 ast_log(LOG_WARNING, "MySQL database sock file not specified. Using default\n");
00384 dbsock = NULL;
00385 }
00386
00387 tmp = ast_variable_retrieve(cfg, "global", "table");
00388 if (tmp) {
00389 dbtable = malloc(strlen(tmp) + 1);
00390 if (dbtable != NULL) {
00391 dbtable_alloc = 1;
00392 strcpy(dbtable, tmp);
00393 } else {
00394 ast_log(LOG_ERROR, "Out of memory error.\n");
00395 return -1;
00396 }
00397 } else {
00398 ast_log(LOG_NOTICE, "MySQL database table not specified. Assuming \"cdr\"\n");
00399 dbtable = "cdr";
00400 }
00401
00402 tmp = ast_variable_retrieve(cfg, "global", "password");
00403 if (tmp) {
00404 password = malloc(strlen(tmp) + 1);
00405 if (password != NULL) {
00406 password_alloc = 1;
00407 strcpy(password, tmp);
00408 } else {
00409 ast_log(LOG_ERROR, "Out of memory error.\n");
00410 return -1;
00411 }
00412 } else {
00413 ast_log(LOG_WARNING, "MySQL database password not specified. Assuming blank\n");
00414 password = "";
00415 }
00416
00417 tmp = ast_variable_retrieve(cfg, "global", "port");
00418 if (tmp) {
00419 if (sscanf(tmp, "%d", &dbport) < 1) {
00420 ast_log(LOG_WARNING, "Invalid MySQL port number. Using default\n");
00421 dbport = 0;
00422 }
00423 }
00424
00425 tmp = ast_variable_retrieve(cfg, "global", "timeout");
00426 if (tmp) {
00427 if (sscanf(tmp,"%d", &timeout) < 1) {
00428 ast_log(LOG_WARNING, "Invalid MySQL timeout number. Using default\n");
00429 timeout = 0;
00430 }
00431 }
00432
00433 tmp = ast_variable_retrieve(cfg, "global", "userfield");
00434 if (tmp) {
00435 if (sscanf(tmp, "%d", &userfield) < 1) {
00436 ast_log(LOG_WARNING, "Invalid MySQL configuration file\n");
00437 userfield = 0;
00438 }
00439 }
00440
00441 ast_config_destroy(cfg);
00442
00443 if (option_debug) {
00444 ast_log(LOG_DEBUG, "cdr_mysql: got hostname of %s\n", hostname);
00445 ast_log(LOG_DEBUG, "cdr_mysql: got port of %d\n", dbport);
00446 ast_log(LOG_DEBUG, "cdr_mysql: got a timeout of %d\n", timeout);
00447 if (dbsock)
00448 ast_log(LOG_DEBUG, "cdr_mysql: got sock file of %s\n", dbsock);
00449 ast_log(LOG_DEBUG, "cdr_mysql: got user of %s\n", dbuser);
00450 ast_log(LOG_DEBUG, "cdr_mysql: got dbname of %s\n", dbname);
00451 ast_log(LOG_DEBUG, "cdr_mysql: got password of %s\n", password);
00452 }
00453
00454 mysql_init(&mysql);
00455
00456 if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
00457 ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00458 }
00459
00460 #if MYSQL_VERSION_ID >= 50013
00461
00462 if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
00463 ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
00464 }
00465 #endif
00466
00467 if (!mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
00468 ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", dbname, hostname);
00469 connected = 0;
00470 records = 0;
00471 } else {
00472 if (option_debug)
00473 ast_log(LOG_DEBUG, "Successfully connected to MySQL database.\n");
00474 connected = 1;
00475 records = 0;
00476 connect_time = time(NULL);
00477 }
00478
00479 res = ast_cdr_register(name, desc, mysql_log);
00480 if (res) {
00481 ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n");
00482 } else {
00483 res = ast_cli_register(&cdr_mysql_status_cli);
00484 }
00485
00486 return res;
00487 }
00488
00489 static int load_module(void)
00490 {
00491 return my_load_module();
00492 }
00493
00494 static int unload_module(void)
00495 {
00496 return my_unload_module();
00497 }
00498
00499 static int reload(void)
00500 {
00501 int ret;
00502
00503 ast_mutex_lock(&mysql_lock);
00504 my_unload_module();
00505 ret = my_load_module();
00506 ast_mutex_unlock(&mysql_lock);
00507
00508 return ret;
00509 }
00510
00511 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MySQL CDR Backend");
00512