cdr_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2003-2005, Digium, Inc.
00005  *
00006  * Brian K. West <brian@bkw.org>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \brief ODBC CDR Backend
00022  *
00023  * \author Brian K. West <brian@bkw.org>
00024  *
00025  * See also:
00026  * \arg http://www.unixodbc.org
00027  * \arg \ref Config_cdr
00028  * \ingroup cdr_drivers
00029  */
00030 
00031 /*! \li \ref cdr_odbc.c uses the configuration file \ref cdr_odbc.conf
00032  * \addtogroup configuration_file Configuration Files
00033  */
00034 
00035 /*!
00036  * \page cdr_odbc.conf cdr_odbc.conf
00037  * \verbinclude cdr_odbc.conf.sample
00038  */
00039 
00040 /*** MODULEINFO
00041    <depend>res_odbc</depend>
00042    <support_level>extended</support_level>
00043  ***/
00044 
00045 #include "asterisk.h"
00046 
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
00048 
00049 #include "asterisk/config.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/cdr.h"
00052 #include "asterisk/module.h"
00053 #include "asterisk/res_odbc.h"
00054 
00055 #define DATE_FORMAT "%Y-%m-%d %T"
00056 
00057 static const char name[] = "ODBC";
00058 static const char config_file[] = "cdr_odbc.conf";
00059 static char *dsn = NULL, *table = NULL;
00060 
00061 enum {
00062    CONFIG_LOGUNIQUEID =       1 << 0,
00063    CONFIG_USEGMTIME =         1 << 1,
00064    CONFIG_DISPOSITIONSTRING = 1 << 2,
00065    CONFIG_HRTIME =            1 << 3,
00066    CONFIG_REGISTERED =        1 << 4,
00067 };
00068 
00069 static struct ast_flags config = { 0 };
00070 
00071 static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
00072 {
00073    struct ast_cdr *cdr = data;
00074    SQLRETURN ODBC_res;
00075    char sqlcmd[2048] = "", timestr[128];
00076    struct ast_tm tm;
00077    SQLHSTMT stmt;
00078 
00079    ast_localtime(&cdr->start, &tm, ast_test_flag(&config, CONFIG_USEGMTIME) ? "GMT" : NULL);
00080    ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
00081 
00082    if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
00083       snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
00084       "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
00085       "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
00086       "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
00087    } else {
00088       snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
00089       "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
00090       "duration,billsec,disposition,amaflags,accountcode) "
00091       "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
00092    }
00093 
00094    ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00095 
00096    if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00097       ast_log(LOG_WARNING, "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
00098       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00099       return NULL;
00100    }
00101 
00102    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL);
00103    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL);
00104    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL);
00105    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL);
00106    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL);
00107    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
00108    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
00109    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
00110 
00111    if (ast_test_flag(&config, CONFIG_HRTIME)) {
00112       double hrbillsec = 0.0;
00113       double hrduration;
00114 
00115       if (!ast_tvzero(cdr->answer)) {
00116          hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0;
00117       }
00118       hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0;
00119 
00120       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrduration, 0, NULL);
00121       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrbillsec, 0, NULL);
00122    } else {
00123       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
00124       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
00125    }
00126 
00127    if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING)) {
00128       char *disposition;
00129       disposition = ast_strdupa(ast_cdr_disp2str(cdr->disposition));
00130       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(disposition) + 1, 0, disposition, 0, NULL);
00131    } else {
00132       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
00133    }
00134    SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
00135    SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
00136 
00137    if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
00138       SQLBindParameter(stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
00139       SQLBindParameter(stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
00140    }
00141 
00142    ODBC_res = SQLExecDirect(stmt, (unsigned char *)sqlcmd, SQL_NTS);
00143 
00144    if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00145       ast_log(LOG_WARNING, "cdr_odbc: Error in ExecDirect: %d, query is: %s\n", ODBC_res, sqlcmd);
00146       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00147       return NULL;
00148    }
00149 
00150    return stmt;
00151 }
00152 
00153 
00154 static int odbc_log(struct ast_cdr *cdr)
00155 {
00156    struct odbc_obj *obj = ast_odbc_request_obj(dsn, 0);
00157    SQLHSTMT stmt;
00158 
00159    if (!obj) {
00160       ast_log(LOG_ERROR, "Unable to retrieve database handle.  CDR failed.\n");
00161       return -1;
00162    }
00163 
00164    stmt = ast_odbc_direct_execute(obj, execute_cb, cdr);
00165    if (stmt) {
00166       SQLLEN rows = 0;
00167 
00168       SQLRowCount(stmt, &rows);
00169       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00170 
00171       if (rows == 0)
00172          ast_log(LOG_WARNING, "CDR successfully ran, but inserted 0 rows?\n");
00173    } else
00174       ast_log(LOG_ERROR, "CDR direct execute failed\n");
00175    ast_odbc_release_obj(obj);
00176    return 0;
00177 }
00178 
00179 static int odbc_load_module(int reload)
00180 {
00181    int res = 0;
00182    struct ast_config *cfg;
00183    struct ast_variable *var;
00184    const char *tmp;
00185    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00186 
00187    do {
00188       cfg = ast_config_load(config_file, config_flags);
00189       if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00190          ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config_file);
00191          res = AST_MODULE_LOAD_DECLINE;
00192          break;
00193       } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00194          break;
00195 
00196       var = ast_variable_browse(cfg, "global");
00197       if (!var) {
00198          /* nothing configured */
00199          break;
00200       }
00201 
00202       if ((tmp = ast_variable_retrieve(cfg, "global", "dsn")) == NULL) {
00203          ast_log(LOG_WARNING, "cdr_odbc: dsn not specified.  Assuming asteriskdb\n");
00204          tmp = "asteriskdb";
00205       }
00206       if (dsn)
00207          ast_free(dsn);
00208       dsn = ast_strdup(tmp);
00209       if (dsn == NULL) {
00210          res = -1;
00211          break;
00212       }
00213 
00214       if (((tmp = ast_variable_retrieve(cfg, "global", "dispositionstring"))) && ast_true(tmp))
00215          ast_set_flag(&config, CONFIG_DISPOSITIONSTRING);
00216       else
00217          ast_clear_flag(&config, CONFIG_DISPOSITIONSTRING);
00218 
00219       if (((tmp = ast_variable_retrieve(cfg, "global", "loguniqueid"))) && ast_true(tmp)) {
00220          ast_set_flag(&config, CONFIG_LOGUNIQUEID);
00221          ast_debug(1, "cdr_odbc: Logging uniqueid\n");
00222       } else {
00223          ast_clear_flag(&config, CONFIG_LOGUNIQUEID);
00224          ast_debug(1, "cdr_odbc: Not logging uniqueid\n");
00225       }
00226 
00227       if (((tmp = ast_variable_retrieve(cfg, "global", "usegmtime"))) && ast_true(tmp)) {
00228          ast_set_flag(&config, CONFIG_USEGMTIME);
00229          ast_debug(1, "cdr_odbc: Logging in GMT\n");
00230       } else {
00231          ast_clear_flag(&config, CONFIG_USEGMTIME);
00232          ast_debug(1, "cdr_odbc: Logging in local time\n");
00233       }
00234 
00235       if (((tmp = ast_variable_retrieve(cfg, "global", "hrtime"))) && ast_true(tmp)) {
00236          ast_set_flag(&config, CONFIG_HRTIME);
00237          ast_debug(1, "cdr_odbc: Logging billsec and duration fields as floats\n");
00238       } else {
00239          ast_clear_flag(&config, CONFIG_HRTIME);
00240          ast_debug(1, "cdr_odbc: Logging billsec and duration fields as integers\n");
00241       }
00242 
00243       if ((tmp = ast_variable_retrieve(cfg, "global", "table")) == NULL) {
00244          ast_log(LOG_WARNING, "cdr_odbc: table not specified.  Assuming cdr\n");
00245          tmp = "cdr";
00246       }
00247       if (table)
00248          ast_free(table);
00249       table = ast_strdup(tmp);
00250       if (table == NULL) {
00251          res = -1;
00252          break;
00253       }
00254 
00255       if (!ast_test_flag(&config, CONFIG_REGISTERED)) {
00256          res = ast_cdr_register(name, ast_module_info->description, odbc_log);
00257          if (res) {
00258             ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
00259          } else {
00260             ast_set_flag(&config, CONFIG_REGISTERED);
00261          }
00262       }
00263    } while (0);
00264 
00265    if (ast_test_flag(&config, CONFIG_REGISTERED) && (!cfg || dsn == NULL || table == NULL)) {
00266       ast_cdr_backend_suspend(name);
00267       ast_clear_flag(&config, CONFIG_REGISTERED);
00268    } else {
00269       ast_cdr_backend_unsuspend(name);
00270    }
00271 
00272    if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID) {
00273       ast_config_destroy(cfg);
00274    }
00275    return res;
00276 }
00277 
00278 static int load_module(void)
00279 {
00280    return odbc_load_module(0);
00281 }
00282 
00283 static int unload_module(void)
00284 {
00285    if (ast_cdr_unregister(name)) {
00286       return -1;
00287    }
00288 
00289    if (dsn) {
00290       ast_free(dsn);
00291    }
00292    if (table) {
00293       ast_free(table);
00294    }
00295 
00296    return 0;
00297 }
00298 
00299 static int reload(void)
00300 {
00301    return odbc_load_module(1);
00302 }
00303 
00304 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "ODBC CDR Backend",
00305       .support_level = AST_MODULE_SUPPORT_EXTENDED,
00306       .load = load_module,
00307       .unload = unload_module,
00308       .reload = reload,
00309       .load_pri = AST_MODPRI_CDR_DRIVER,
00310           );

Generated on Thu Apr 16 06:27:17 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6