Wed Apr 6 11:29:41 2011

Asterisk developer's documentation


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 /*** MODULEINFO
00032    <depend>res_odbc</depend>
00033  ***/
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 283319 $")
00038 
00039 #include "asterisk/config.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/cdr.h"
00042 #include "asterisk/module.h"
00043 #include "asterisk/res_odbc.h"
00044 
00045 #define DATE_FORMAT "%Y-%m-%d %T"
00046 
00047 static const char name[] = "ODBC";
00048 static const char config_file[] = "cdr_odbc.conf";
00049 static char *dsn = NULL, *table = NULL;
00050 
00051 enum {
00052    CONFIG_LOGUNIQUEID =       1 << 0,
00053    CONFIG_USEGMTIME =         1 << 1,
00054    CONFIG_DISPOSITIONSTRING = 1 << 2,
00055    CONFIG_HRTIME =            1 << 3,
00056    CONFIG_REGISTERED =        1 << 4,
00057 };
00058 
00059 static struct ast_flags config = { 0 };
00060 
00061 static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
00062 {
00063    struct ast_cdr *cdr = data;
00064    SQLRETURN ODBC_res;
00065    char sqlcmd[2048] = "", timestr[128];
00066    struct ast_tm tm;
00067    SQLHSTMT stmt;
00068 
00069    ast_localtime(&cdr->start, &tm, ast_test_flag(&config, CONFIG_USEGMTIME) ? "GMT" : NULL);
00070    ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
00071 
00072    if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
00073       snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
00074       "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
00075       "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
00076       "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
00077    } else {
00078       snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
00079       "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
00080       "duration,billsec,disposition,amaflags,accountcode) "
00081       "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
00082    }
00083 
00084    ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00085 
00086    if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00087       ast_verb(11, "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
00088       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00089       return NULL;
00090    }
00091 
00092    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL);
00093    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL);
00094    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL);
00095    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL);
00096    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL);
00097    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
00098    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
00099    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
00100 
00101    if (ast_test_flag(&config, CONFIG_HRTIME)) {
00102       double hrbillsec = 0.0;
00103       double hrduration;
00104 
00105       if (!ast_tvzero(cdr->answer)) {
00106          hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0;
00107       }
00108       hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0;
00109 
00110       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrbillsec, 0, NULL);
00111       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrduration, 0, NULL);
00112    } else {
00113       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
00114       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
00115    }
00116 
00117    if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
00118       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
00119    else
00120       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
00121    SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
00122    SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
00123 
00124    if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
00125       SQLBindParameter(stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
00126       SQLBindParameter(stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
00127    }
00128 
00129    ODBC_res = SQLExecDirect(stmt, (unsigned char *)sqlcmd, SQL_NTS);
00130 
00131    if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00132       ast_verb(11, "cdr_odbc: Error in ExecDirect: %d\n", ODBC_res);
00133       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00134       return NULL;
00135    }
00136 
00137    return stmt;
00138 }
00139 
00140 
00141 static int odbc_log(struct ast_cdr *cdr)
00142 {
00143    struct odbc_obj *obj = ast_odbc_request_obj(dsn, 0);
00144    SQLHSTMT stmt;
00145 
00146    if (!obj) {
00147       ast_log(LOG_ERROR, "Unable to retrieve database handle.  CDR failed.\n");
00148       return -1;
00149    }
00150 
00151    stmt = ast_odbc_direct_execute(obj, execute_cb, cdr);
00152    if (stmt) {
00153       SQLLEN rows = 0;
00154 
00155       SQLRowCount(stmt, &rows);
00156       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00157 
00158       if (rows == 0)
00159          ast_log(LOG_WARNING, "CDR successfully ran, but inserted 0 rows?\n");
00160    } else
00161       ast_log(LOG_ERROR, "CDR direct execute failed\n");
00162    ast_odbc_release_obj(obj);
00163    return 0;
00164 }
00165 
00166 static int odbc_load_module(int reload)
00167 {
00168    int res = 0;
00169    struct ast_config *cfg;
00170    struct ast_variable *var;
00171    const char *tmp;
00172    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00173 
00174    do {
00175       cfg = ast_config_load(config_file, config_flags);
00176       if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00177          ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config_file);
00178          res = AST_MODULE_LOAD_DECLINE;
00179          break;
00180       } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00181          break;
00182 
00183       var = ast_variable_browse(cfg, "global");
00184       if (!var) {
00185          /* nothing configured */
00186          break;
00187       }
00188 
00189       if ((tmp = ast_variable_retrieve(cfg, "global", "dsn")) == NULL) {
00190          ast_log(LOG_WARNING, "cdr_odbc: dsn not specified.  Assuming asteriskdb\n");
00191          tmp = "asteriskdb";
00192       }
00193       if (dsn)
00194          ast_free(dsn);
00195       dsn = ast_strdup(tmp);
00196       if (dsn == NULL) {
00197          res = -1;
00198          break;
00199       }
00200 
00201       if (((tmp = ast_variable_retrieve(cfg, "global", "dispositionstring"))) && ast_true(tmp))
00202          ast_set_flag(&config, CONFIG_DISPOSITIONSTRING);
00203       else
00204          ast_clear_flag(&config, CONFIG_DISPOSITIONSTRING);
00205 
00206       if (((tmp = ast_variable_retrieve(cfg, "global", "loguniqueid"))) && ast_true(tmp)) {
00207          ast_set_flag(&config, CONFIG_LOGUNIQUEID);
00208          ast_debug(1, "cdr_odbc: Logging uniqueid\n");
00209       } else {
00210          ast_clear_flag(&config, CONFIG_LOGUNIQUEID);
00211          ast_debug(1, "cdr_odbc: Not logging uniqueid\n");
00212       }
00213 
00214       if (((tmp = ast_variable_retrieve(cfg, "global", "usegmtime"))) && ast_true(tmp)) {
00215          ast_set_flag(&config, CONFIG_USEGMTIME);
00216          ast_debug(1, "cdr_odbc: Logging in GMT\n");
00217       } else {
00218          ast_clear_flag(&config, CONFIG_USEGMTIME);
00219          ast_debug(1, "cdr_odbc: Logging in local time\n");
00220       }
00221 
00222       if (((tmp = ast_variable_retrieve(cfg, "global", "hrtime"))) && ast_true(tmp)) {
00223          ast_set_flag(&config, CONFIG_HRTIME);
00224          ast_debug(1, "cdr_odbc: Logging billsec and duration fields as floats\n");
00225       } else {
00226          ast_clear_flag(&config, CONFIG_HRTIME);
00227          ast_debug(1, "cdr_odbc: Logging billsec and duration fields as integers\n");
00228       }
00229 
00230       if ((tmp = ast_variable_retrieve(cfg, "global", "table")) == NULL) {
00231          ast_log(LOG_WARNING, "cdr_odbc: table not specified.  Assuming cdr\n");
00232          tmp = "cdr";
00233       }
00234       if (table)
00235          ast_free(table);
00236       table = ast_strdup(tmp);
00237       if (table == NULL) {
00238          res = -1;
00239          break;
00240       }
00241 
00242       ast_verb(3, "cdr_odbc: dsn is %s\n", dsn);
00243       ast_verb(3, "cdr_odbc: table is %s\n", table);
00244 
00245       if (!ast_test_flag(&config, CONFIG_REGISTERED)) {
00246          res = ast_cdr_register(name, ast_module_info->description, odbc_log);
00247          if (res) {
00248             ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
00249          } else {
00250             ast_set_flag(&config, CONFIG_REGISTERED);
00251          }
00252       }
00253    } while (0);
00254 
00255    if (ast_test_flag(&config, CONFIG_REGISTERED) && (!cfg || dsn == NULL || table == NULL)) {
00256       ast_cdr_unregister(name);
00257       ast_clear_flag(&config, CONFIG_REGISTERED);
00258    }
00259 
00260    if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID) {
00261       ast_config_destroy(cfg);
00262    }
00263    return res;
00264 }
00265 
00266 static int load_module(void)
00267 {
00268    return odbc_load_module(0);
00269 }
00270 
00271 static int unload_module(void)
00272 {
00273    ast_cdr_unregister(name);
00274 
00275    if (dsn) {
00276       ast_verb(11, "cdr_odbc: free dsn\n");
00277       ast_free(dsn);
00278    }
00279    if (table) {
00280       ast_verb(11, "cdr_odbc: free table\n");
00281       ast_free(table);
00282    }
00283 
00284    return 0;
00285 }
00286 
00287 static int reload(void)
00288 {
00289    return odbc_load_module(1);
00290 }
00291 
00292 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "ODBC CDR Backend",
00293       .load = load_module,
00294       .unload = unload_module,
00295       .reload = reload,
00296       .load_pri = AST_MODPRI_CDR_DRIVER,
00297           );

Generated on Wed Apr 6 11:29:41 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7