/* * Asterisk -- A telephony toolkit for Linux. * * MySQL CDR logger * * Matteo Brancaleoni * based on the first version by * James Sharp * * This program is free software, distributed under the terms of * the GNU General Public License. * * * Table Structure for `cdr` * * Created on: 07 Giu, 2003 at 02:57 * Last changed on: 07 Giu, 2003 at 03:07 CREATE TABLE `cdr` ( `ID` int(10) unsigned NOT NULL auto_increment, `calldate` datetime NOT NULL default '0000-00-00 00:00:00', `clid` varchar(80) NOT NULL default '', `src` varchar(80) NOT NULL default '', `dst` varchar(80) NOT NULL default '', `dcontext` varchar(80) NOT NULL default '', `channel` varchar(80) NOT NULL default '', `dstchannel` varchar(80) NOT NULL default '', `lastapp` varchar(80) NOT NULL default '', `lastdata` varchar(80) NOT NULL default '', `start` datetime NOT NULL default '0000-00-00 00:00:00', `answer` datetime NOT NULL default '0000-00-00 00:00:00', `end` datetime NOT NULL default '0000-00-00 00:00:00', `duration` int(11) NOT NULL default '0', `billsec` int(11) NOT NULL default '0', `disposition` int(11) NOT NULL default '0', `amaflags` int(11) NOT NULL default '0', `accountcode` varchar(20) NOT NULL default '', `uniqueid` varchar(32) NOT NULL default '', PRIMARY KEY (`ID`) ) TYPE=MyISAM AUTO_INCREMENT=2 ; */ #include #include #include #include #include #include #include #include "../asterisk.h" #include #include #include #include #include #include #include #define DATE_FORMAT "%Y-%m-%d %T" #define NFIELDS (sizeof(field_sizes)/sizeof(field_sizes[0])) static char *desc = "MySQL CDR Backend"; static char *name = "mysql"; static char *config = "cdr_mysql.conf"; static MYSQL *mysql; /* as from cdr.h */ struct fsize { char *field; int length; char *alias; } field_sizes[] = { {"calldate",19,"timestr"}, {"start",19,"startstr"}, {"end",19,"endstr"}, {"answer",19,"answerstr"}, {"clid",80,""}, {"src",80,""}, {"dst",80,""}, {"dcontext",80,""}, {"channel",80,""}, {"dstchannel",80,""}, {"lastapp",80,""}, {"lastdata",80,""}, {"accountcode",20,""}, {"uniqueid",32,""}, {"duration",11,""}, {"billsec",11,""}, {"disposition",11,""}, {"amaflags",11,""} }; static int fields_walk(char *, struct fsize *, int); static int mysql_sanity_check(MYSQL *tmpdb) { int cnt,n; static MYSQL_RES *mysql2; mysql2 = mysql_list_fields(tmpdb,"cdr","%"); if (mysql2 == NULL) { ast_log(LOG_ERROR, "Failed to list cdr records.\n"); return -1; } else { ast_log(LOG_DEBUG,"Database fields listed!.\n"); cnt=1; while(cnt <= mysql2->field_count) { if((n=fields_walk(mysql2->fields->name,field_sizes,NFIELDS)) >= 0) { if (mysql2->fields->length < field_sizes[n].length) { ast_log(LOG_WARNING,"Field %s is smaller than default %d. Data loss possibility! Lowering to %d\n",mysql2->fields->name,field_sizes[n].length,mysql2->fields->length); field_sizes[n].length=mysql2->fields->length; } else if (mysql2->fields->length > field_sizes[n].length) { ast_log(LOG_DEBUG,"Field %s is bigger than default %d. Raising to %d\n",mysql2->fields->name,field_sizes[n].length,mysql2->fields->length); field_sizes[n].length=mysql2->fields->length; } } mysql2->fields++; cnt++; } /* end while */ } mysql_free_result(mysql2); return 0; } static int fields_walk(char *fname, struct fsize tmp[], int n) { /* this snippet of code has been taken from "The C programming Language" 2nd edition */ int cond; int low, high; low = 0; high = n-1; while (low <= high) { if (!(cond=strcmp(fname, tmp[low].field)) || !(cond=strcmp(fname, tmp[low].alias))) return low; else low++; } ast_log(LOG_WARNING,"Why I'm here? fname %s\n",fname); return -1; } static int mysql_log(struct ast_cdr *cdr) { struct tm tm; struct timeval tv; struct timezone tz; char *sqlcmd, timestr[128], startstr[80], answerstr[80], endstr[80]; time_t t; int n,l; sqlcmd = (char *)malloc(2048); if (sqlcmd == NULL) { ast_log(LOG_ERROR,"Failed to malloc for the query string."); return -1; } memset(sqlcmd,0,2048); /* Does we really need calldate ??? */ gettimeofday(&tv,&tz); t = tv.tv_sec; localtime_r(&t,&tm); strftime(timestr,sizeof(timestr),DATE_FORMAT,&tm); /* set start */ t = cdr->start.tv_sec; localtime_r(&t,&tm); strftime(startstr,sizeof(startstr),DATE_FORMAT,&tm); /* set answer */ t = cdr->answer.tv_sec; localtime_r(&t,&tm); strftime(answerstr,sizeof(answerstr),DATE_FORMAT,&tm); /* set end */ t = cdr->end.tv_sec; localtime_r(&t,&tm); strftime(endstr,sizeof(endstr),DATE_FORMAT,&tm); /* Start strings len checks, to make mysql happy & generate warnings */ n=fields_walk("timestr",field_sizes,NFIELDS); if ((strlen(timestr) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); timestr[l]='\0'; } n=fields_walk("clid",field_sizes,NFIELDS); if ((strlen(cdr->clid) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->clid[l]='\0'; } n=fields_walk("src",field_sizes,NFIELDS); if ((strlen(cdr->src) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->src[l]='\0'; } n=fields_walk("dst",field_sizes,NFIELDS); if ((strlen(cdr->dst) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->dst[l]='\0'; } n=fields_walk("dcontext",field_sizes,NFIELDS); if ((strlen(cdr->dcontext) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->dcontext[l]='\0'; } n=fields_walk("channel",field_sizes,NFIELDS); if ((strlen(cdr->channel) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->channel[l]='\0'; } n=fields_walk("dstchannel",field_sizes,NFIELDS); if ((strlen(cdr->dstchannel) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->dstchannel[l]='\0'; } n=fields_walk("lastapp",field_sizes,NFIELDS); if ((strlen(cdr->lastapp) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->lastapp[l]='\0'; } n=fields_walk("lastdata",field_sizes,NFIELDS); if ((strlen(cdr->lastdata) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->lastdata[l]='\0'; } n=fields_walk("startstr",field_sizes,NFIELDS); if ((strlen(startstr) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); startstr[l]='\0'; } n=fields_walk("answerstr",field_sizes,NFIELDS); if ((strlen(answerstr) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); answerstr[l]='\0'; } n=fields_walk("endstr",field_sizes,NFIELDS); if ((strlen(endstr) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); endstr[l]='\0'; } n=fields_walk("duration",field_sizes,NFIELDS); if (cdr->duration > (pow(10,l=field_sizes[n].length)-1) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->duration=-1; } n=fields_walk("billsec",field_sizes,NFIELDS); if (cdr->billsec > (pow(10,l=field_sizes[n].length)-1) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->billsec=-1; } n=fields_walk("disposition",field_sizes,NFIELDS); if (cdr->disposition > (pow(10,l=field_sizes[n].length)-1) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->disposition=-1; } n=fields_walk("amaflags",field_sizes,NFIELDS); if (cdr->amaflags > (pow(10,l=field_sizes[n].length)-1) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->amaflags=-1; } n=fields_walk("accountcode",field_sizes,NFIELDS); if ((strlen(cdr->accountcode) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->accountcode[l]='\0'; } n=fields_walk("uniqueid",field_sizes,NFIELDS); if ((strlen(cdr->uniqueid) > (l=field_sizes[n].length)) && (n >= 0)) { ast_log(LOG_WARNING,"Data Loss! Cutting %s\n",field_sizes[n].field); cdr->uniqueid[l]='\0'; } /* Start the query .... */ ast_log(LOG_DEBUG,"cdr_mysql: inserting a CDR record.\n"); sprintf(sqlcmd,"INSERT INTO cdr (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,start,answer,end,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s','%s','%s','%s',%i,%i,%i,%i,'%s','%s')",timestr,cdr->clid,cdr->src, cdr->dst, cdr->dcontext,cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, startstr, answerstr, endstr,cdr->duration,cdr->billsec,cdr->disposition,cdr->amaflags, cdr->accountcode, cdr->uniqueid); ast_log(LOG_DEBUG,"cdr_mysql: SQL command as follows: %s\n",sqlcmd); if (mysql_real_query(mysql,sqlcmd,strlen(sqlcmd))) { ast_log(LOG_ERROR,"Failed to insert into database."); free(sqlcmd); return -1; } free(sqlcmd); return 0; } char *description(void) { return desc; } int unload_module(void) { mysql_close(mysql); ast_cdr_unregister(name); return 0; } int load_module(void) { int res; struct ast_config *cfg; struct ast_variable *var; char *hostname, *dbname, *dbuser, *password; cfg = ast_load(config); if (!cfg) { ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config); return 0; } var = ast_variable_browse(cfg, "global"); if (!var) { /* nothing configured */ return 0; } hostname = ast_variable_retrieve(cfg,"global","hostname"); dbname = ast_variable_retrieve(cfg,"global","dbname"); dbuser = ast_variable_retrieve(cfg,"global","user"); password = ast_variable_retrieve(cfg,"global","password"); ast_log(LOG_DEBUG,"cdr_mysql: got hostname of %s\n",hostname); ast_log(LOG_DEBUG,"cdr_mysql: got user of %s\n",dbuser); ast_log(LOG_DEBUG,"cdr_mysql: got dbname of %s\n",dbname); ast_log(LOG_DEBUG,"cdr_mysql: got password of %s\n",password); if (hostname == NULL) { ast_log(LOG_ERROR,"Database server hostname not specified.\n"); return -1; } if (dbuser == NULL) { ast_log(LOG_ERROR,"Database dbuser not specified.\n"); return -1; } if (dbname == NULL) { ast_log(LOG_ERROR,"Database dbname not specified.\n"); return -1; } if (password == NULL) { ast_log(LOG_ERROR,"Database password not specified.\n"); return -1; } mysql = mysql_init(NULL); mysql = mysql_real_connect(mysql, hostname, dbuser, password, dbname, 0, NULL, 0); if (mysql == NULL) { ast_log(LOG_ERROR, "Failed to connect to mysql database.\n"); return -1; } else { ast_log(LOG_DEBUG,"Successfully connected to MySQL database.\n"); } mysql_sanity_check(mysql); res = ast_cdr_register(name, desc, mysql_log); if (res) { ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n"); } return res; } int reload(void) { struct ast_config *cfg; struct ast_variable *var; char *hostname, *dbname, *password, *dbuser; mysql_close(mysql); cfg = ast_load(config); if (!cfg) { ast_log(LOG_WARNING, "Unable to load MySQL CDR config %s\n", config); return 0; } var = ast_variable_browse(cfg, "global"); if (!var) { /* nothing configured */ return 0; } hostname = ast_variable_retrieve(cfg,"global","hostname"); dbname = ast_variable_retrieve(cfg,"global","dbname"); dbuser = ast_variable_retrieve(cfg,"global","user"); password = ast_variable_retrieve(cfg,"global","password"); ast_log(LOG_DEBUG,"cdr_mysql: got hostname of %s\n",hostname); ast_log(LOG_DEBUG,"cdr_mysql: got dbname of %s\n",dbname); ast_log(LOG_DEBUG,"cdr_mysql: got dbuser of %s\n",dbuser); ast_log(LOG_DEBUG,"cdr_mysql: got password of %s\n",password); if (hostname == NULL) { ast_log(LOG_ERROR,"Database server hostname not specified.\n"); return -1; } if (dbname == NULL) { ast_log(LOG_ERROR,"Database dbname not specified.\n"); return -1; } if (dbuser == NULL) { ast_log(LOG_ERROR,"Database dbuser not specified.\n"); return -1; } if (password == NULL) { ast_log(LOG_ERROR,"Database password not specified.\n"); return -1; } mysql = mysql_init(NULL); mysql = mysql_real_connect(mysql, hostname, dbuser, password, dbname, 0, NULL, 0); if (mysql == NULL) { ast_log(LOG_ERROR, "Failed to connect to mysql database.\n"); return -1; } else { ast_log(LOG_DEBUG,"Successfully connected to MySQL database.\n"); } mysql_sanity_check(mysql); return 0; } int usecount(void) { return 0; } char *key() { return ASTERISK_GPL_KEY; }