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
00026
00027
00028
00029
00030
00031
00032
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
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 );