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