#include "asterisk.h"
#include <sys/types.h>
#include <time.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/res_odbc.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
Go to the source code of this file.
Data Structures | |
struct | columns |
struct | odbc_tables |
struct | tables |
struct | tables::odbc_columns |
Defines | |
#define | CONFIG "cdr_adaptive_odbc.conf" |
#define | LENGTHEN_BUF1(size) |
#define | LENGTHEN_BUF2(size) |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | free_config (void) |
static SQLHSTMT | generic_prepare (struct odbc_obj *obj, void *data) |
static int | load_config (void) |
static int | load_module (void) |
static int | odbc_log (struct ast_cdr *cdr) |
static int | reload (void) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Adaptive ODBC CDR backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } |
static struct ast_module_info * | ast_module_info = &__mod_info |
static int | maxsize = 512 |
static int | maxsize2 = 512 |
static const char | name [] = "Adaptive ODBC" |
Definition in file cdr_adaptive_odbc.c.
#define CONFIG "cdr_adaptive_odbc.conf" |
#define LENGTHEN_BUF1 | ( | size | ) |
#define LENGTHEN_BUF2 | ( | size | ) |
static void __reg_module | ( | void | ) | [static] |
Definition at line 761 of file cdr_adaptive_odbc.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 761 of file cdr_adaptive_odbc.c.
static int free_config | ( | void | ) | [static] |
Definition at line 268 of file cdr_adaptive_odbc.c.
References ast_free, AST_LIST_REMOVE_HEAD, AST_RWLIST_REMOVE_HEAD, columns::list, and table.
Referenced by load_config(), load_module(), reload(), and unload_module().
00269 { 00270 struct tables *table; 00271 struct columns *entry; 00272 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { 00273 while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) { 00274 ast_free(entry); 00275 } 00276 ast_free(table); 00277 } 00278 return 0; 00279 }
static SQLHSTMT generic_prepare | ( | struct odbc_obj * | obj, | |
void * | data | |||
) | [static] |
Definition at line 281 of file cdr_adaptive_odbc.c.
References ast_log(), odbc_obj::con, and LOG_WARNING.
Referenced by odbc_log().
00282 { 00283 int res, i; 00284 SQLHSTMT stmt; 00285 SQLINTEGER nativeerror = 0, numfields = 0; 00286 SQLSMALLINT diagbytes = 0; 00287 unsigned char state[10], diagnostic[256]; 00288 00289 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt); 00290 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00291 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n"); 00292 return NULL; 00293 } 00294 00295 res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS); 00296 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00297 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data); 00298 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00299 for (i = 0; i < numfields; i++) { 00300 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00301 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00302 if (i > 10) { 00303 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00304 break; 00305 } 00306 } 00307 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00308 return NULL; 00309 } 00310 00311 return stmt; 00312 }
static int load_config | ( | void | ) | [static] |
Definition at line 80 of file cdr_adaptive_odbc.c.
References ast_calloc, ast_category_browse(), ast_config_load, ast_copy_string(), AST_LIST_INSERT_TAIL, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj, ast_strdupa, ast_strip(), ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, tables::columns, columns, odbc_obj::con, CONFIG, config_flags, CONFIG_STATUS_FILEINVALID, tables::connection, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::staticvalue, tables::table, usegmtime, and var.
00081 { 00082 struct ast_config *cfg; 00083 struct ast_variable *var; 00084 const char *tmp, *catg; 00085 struct tables *tableptr; 00086 struct columns *entry; 00087 struct odbc_obj *obj; 00088 char columnname[80]; 00089 char connection[40]; 00090 char table[40]; 00091 int lenconnection, lentable, usegmtime = 0; 00092 SQLLEN sqlptr; 00093 int res = 0; 00094 SQLHSTMT stmt = NULL; 00095 struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */ 00096 00097 cfg = ast_config_load(CONFIG, config_flags); 00098 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { 00099 ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n"); 00100 return -1; 00101 } 00102 00103 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { 00104 var = ast_variable_browse(cfg, catg); 00105 if (!var) 00106 continue; 00107 00108 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) { 00109 ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg); 00110 continue; 00111 } 00112 ast_copy_string(connection, tmp, sizeof(connection)); 00113 lenconnection = strlen(connection); 00114 00115 if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) { 00116 usegmtime = ast_true(tmp); 00117 } 00118 00119 /* When loading, we want to be sure we can connect. */ 00120 obj = ast_odbc_request_obj(connection, 1); 00121 if (!obj) { 00122 ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg); 00123 continue; 00124 } 00125 00126 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) { 00127 ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n"); 00128 tmp = "cdr"; 00129 } 00130 ast_copy_string(table, tmp, sizeof(table)); 00131 lentable = strlen(table); 00132 00133 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00134 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00135 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection); 00136 ast_odbc_release_obj(obj); 00137 continue; 00138 } 00139 00140 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS); 00141 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00142 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection); 00143 ast_odbc_release_obj(obj); 00144 continue; 00145 } 00146 00147 tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1); 00148 if (!tableptr) { 00149 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection); 00150 ast_odbc_release_obj(obj); 00151 res = -1; 00152 break; 00153 } 00154 00155 tableptr->usegmtime = usegmtime; 00156 tableptr->connection = (char *)tableptr + sizeof(*tableptr); 00157 tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1; 00158 ast_copy_string(tableptr->connection, connection, lenconnection + 1); 00159 ast_copy_string(tableptr->table, table, lentable + 1); 00160 00161 ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection); 00162 00163 /* Check for filters first */ 00164 for (var = ast_variable_browse(cfg, catg); var; var = var->next) { 00165 if (strncmp(var->name, "filter", 6) == 0) { 00166 char *cdrvar = ast_strdupa(var->name + 6); 00167 cdrvar = ast_strip(cdrvar); 00168 ast_verb(3, "Found filter %s for cdr variable %s in %s@%s\n", var->value, cdrvar, tableptr->table, tableptr->connection); 00169 00170 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1); 00171 if (!entry) { 00172 ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection); 00173 res = -1; 00174 break; 00175 } 00176 00177 /* NULL column entry means this isn't a column in the database */ 00178 entry->name = NULL; 00179 entry->cdrname = (char *)entry + sizeof(*entry); 00180 entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1; 00181 strcpy(entry->cdrname, cdrvar); 00182 strcpy(entry->filtervalue, var->value); 00183 00184 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00185 } 00186 } 00187 00188 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) { 00189 char *cdrvar = "", *staticvalue = ""; 00190 00191 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr); 00192 00193 /* Is there an alias for this column? */ 00194 00195 /* NOTE: This seems like a non-optimal parse method, but I'm going 00196 * for user configuration readability, rather than fast parsing. We 00197 * really don't parse this file all that often, anyway. 00198 */ 00199 for (var = ast_variable_browse(cfg, catg); var; var = var->next) { 00200 if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) { 00201 char *alias = ast_strdupa(var->name + 5); 00202 cdrvar = ast_strip(alias); 00203 ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection); 00204 break; 00205 } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) { 00206 char *item = ast_strdupa(var->name + 6); 00207 item = ast_strip(item); 00208 if (item[0] == '"' && item[strlen(item) - 1] == '"') { 00209 /* Remove surrounding quotes */ 00210 item[strlen(item) - 1] = '\0'; 00211 item++; 00212 } 00213 staticvalue = item; 00214 } 00215 } 00216 00217 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1); 00218 if (!entry) { 00219 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection); 00220 res = -1; 00221 break; 00222 } 00223 entry->name = (char *)entry + sizeof(*entry); 00224 strcpy(entry->name, columnname); 00225 00226 if (!ast_strlen_zero(cdrvar)) { 00227 entry->cdrname = entry->name + strlen(columnname) + 1; 00228 strcpy(entry->cdrname, cdrvar); 00229 } else { /* Point to same place as the column name */ 00230 entry->cdrname = (char *)entry + sizeof(*entry); 00231 } 00232 00233 if (!ast_strlen_zero(staticvalue)) { 00234 entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1; 00235 strcpy(entry->staticvalue, staticvalue); 00236 } 00237 00238 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL); 00239 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL); 00240 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL); 00241 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL); 00242 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL); 00243 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL); 00244 00245 /* Specification states that the octenlen should be the maximum number of bytes 00246 * returned in a char or binary column, but it seems that some drivers just set 00247 * it to NULL. (Bad Postgres! No biscuit!) */ 00248 if (entry->octetlen == 0) 00249 entry->octetlen = entry->size; 00250 00251 ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix); 00252 /* Insert column info into column list */ 00253 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); 00254 res = 0; 00255 } 00256 00257 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00258 ast_odbc_release_obj(obj); 00259 00260 if (AST_LIST_FIRST(&(tableptr->columns))) 00261 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); 00262 else 00263 ast_free(tableptr); 00264 } 00265 return res; 00266 }
static int load_module | ( | void | ) | [static] |
Definition at line 730 of file cdr_adaptive_odbc.c.
References ast_cdr_register(), ast_log(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, load_config(), LOG_ERROR, and odbc_log().
00731 { 00732 if (AST_RWLIST_WRLOCK(&odbc_tables)) { 00733 ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n"); 00734 return 0; 00735 } 00736 00737 load_config(); 00738 AST_RWLIST_UNLOCK(&odbc_tables); 00739 ast_cdr_register(name, ast_module_info->description, odbc_log); 00740 return 0; 00741 }
static int odbc_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 341 of file cdr_adaptive_odbc.c.
References ast_cdr::answer, ast_cdr_getvar(), ast_copy_string(), ast_free, AST_LIST_TRAVERSE, ast_localtime(), ast_log(), ast_odbc_backslash_is_escape(), ast_odbc_prepare_and_execute(), ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_strlen(), ast_strdupa, ast_strftime(), ast_strlen_zero(), ast_tvdiff_us(), ast_tvzero(), ast_verb, columns::cdrname, tables::columns, tables::connection, columns::decimals, ast_cdr::end, columns::filtervalue, first, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, columns::octetlen, columns::radix, ast_cdr::start, columns::staticvalue, tables::table, columns::type, and tables::usegmtime.
Referenced by load_module(), odbc_load_module(), and unload_module().
00342 { 00343 struct tables *tableptr; 00344 struct columns *entry; 00345 struct odbc_obj *obj; 00346 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); 00347 char *tmp; 00348 char colbuf[1024], *colptr; 00349 SQLHSTMT stmt = NULL; 00350 SQLLEN rows = 0; 00351 00352 if (!sql || !sql2) { 00353 if (sql) 00354 ast_free(sql); 00355 if (sql2) 00356 ast_free(sql2); 00357 return -1; 00358 } 00359 00360 if (AST_RWLIST_RDLOCK(&odbc_tables)) { 00361 ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n"); 00362 ast_free(sql); 00363 ast_free(sql2); 00364 return -1; 00365 } 00366 00367 AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) { 00368 int first = 1; 00369 ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table); 00370 ast_str_set(&sql2, 0, " VALUES ("); 00371 00372 /* No need to check the connection now; we'll handle any failure in prepare_and_execute */ 00373 if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) { 00374 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql)); 00375 continue; 00376 } 00377 00378 AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) { 00379 int datefield = 0; 00380 if (strcasecmp(entry->cdrname, "start") == 0) { 00381 datefield = 1; 00382 } else if (strcasecmp(entry->cdrname, "answer") == 0) { 00383 datefield = 2; 00384 } else if (strcasecmp(entry->cdrname, "end") == 0) { 00385 datefield = 3; 00386 } 00387 00388 /* Check if we have a similarly named variable */ 00389 if (entry->staticvalue) { 00390 colptr = ast_strdupa(entry->staticvalue); 00391 } else if (datefield && tableptr->usegmtime) { 00392 struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end; 00393 struct ast_tm tm = { 0, }; 00394 ast_localtime(&date_tv, &tm, "UTC"); 00395 ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm); 00396 colptr = colbuf; 00397 } else { 00398 ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1); 00399 } 00400 00401 if (colptr) { 00402 /* Check first if the column filters this entry. Note that this 00403 * is very specifically NOT ast_strlen_zero(), because the filter 00404 * could legitimately specify that the field is blank, which is 00405 * different from the field being unspecified (NULL). */ 00406 if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) { 00407 ast_verb(4, "CDR column '%s' with value '%s' does not match filter of" 00408 " '%s'. Cancelling this CDR.\n", 00409 entry->cdrname, colptr, entry->filtervalue); 00410 goto early_release; 00411 } 00412 00413 /* Only a filter? */ 00414 if (ast_strlen_zero(entry->name)) 00415 continue; 00416 00417 LENGTHEN_BUF1(strlen(entry->name)); 00418 00419 switch (entry->type) { 00420 case SQL_CHAR: 00421 case SQL_VARCHAR: 00422 case SQL_LONGVARCHAR: 00423 case SQL_BINARY: 00424 case SQL_VARBINARY: 00425 case SQL_LONGVARBINARY: 00426 case SQL_GUID: 00427 /* For these two field names, get the rendered form, instead of the raw 00428 * form (but only when we're dealing with a character-based field). 00429 */ 00430 if (strcasecmp(entry->name, "disposition") == 0) { 00431 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0); 00432 } else if (strcasecmp(entry->name, "amaflags") == 0) { 00433 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0); 00434 } 00435 00436 /* Truncate too-long fields */ 00437 if (entry->type != SQL_GUID) { 00438 if (strlen(colptr) > entry->octetlen) { 00439 colptr[entry->octetlen] = '\0'; 00440 } 00441 } 00442 00443 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00444 LENGTHEN_BUF2(strlen(colptr)); 00445 00446 /* Encode value, with escaping */ 00447 ast_str_append(&sql2, 0, "%s'", first ? "" : ","); 00448 for (tmp = colptr; *tmp; tmp++) { 00449 if (*tmp == '\'') { 00450 ast_str_append(&sql2, 0, "''"); 00451 } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) { 00452 ast_str_append(&sql2, 0, "\\\\"); 00453 } else { 00454 ast_str_append(&sql2, 0, "%c", *tmp); 00455 } 00456 } 00457 ast_str_append(&sql2, 0, "'"); 00458 break; 00459 case SQL_TYPE_DATE: 00460 if (ast_strlen_zero(colptr)) { 00461 continue; 00462 } else { 00463 int year = 0, month = 0, day = 0; 00464 if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 || 00465 month <= 0 || month > 12 || day < 0 || day > 31 || 00466 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) || 00467 (month == 2 && year % 400 == 0 && day > 29) || 00468 (month == 2 && year % 100 == 0 && day > 28) || 00469 (month == 2 && year % 4 == 0 && day > 29) || 00470 (month == 2 && year % 4 != 0 && day > 28)) { 00471 ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr); 00472 continue; 00473 } 00474 00475 if (year > 0 && year < 100) { 00476 year += 2000; 00477 } 00478 00479 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00480 LENGTHEN_BUF2(17); 00481 ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day); 00482 } 00483 break; 00484 case SQL_TYPE_TIME: 00485 if (ast_strlen_zero(colptr)) { 00486 continue; 00487 } else { 00488 int hour = 0, minute = 0, second = 0; 00489 int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second); 00490 00491 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) { 00492 ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr); 00493 continue; 00494 } 00495 00496 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00497 LENGTHEN_BUF2(15); 00498 ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second); 00499 } 00500 break; 00501 case SQL_TYPE_TIMESTAMP: 00502 case SQL_TIMESTAMP: 00503 if (ast_strlen_zero(colptr)) { 00504 continue; 00505 } else { 00506 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; 00507 int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second); 00508 00509 if ((count != 3 && count != 5 && count != 6) || year <= 0 || 00510 month <= 0 || month > 12 || day < 0 || day > 31 || 00511 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) || 00512 (month == 2 && year % 400 == 0 && day > 29) || 00513 (month == 2 && year % 100 == 0 && day > 28) || 00514 (month == 2 && year % 4 == 0 && day > 29) || 00515 (month == 2 && year % 4 != 0 && day > 28) || 00516 hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) { 00517 ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr); 00518 continue; 00519 } 00520 00521 if (year > 0 && year < 100) { 00522 year += 2000; 00523 } 00524 00525 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00526 LENGTHEN_BUF2(26); 00527 ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second); 00528 } 00529 break; 00530 case SQL_INTEGER: 00531 if (ast_strlen_zero(colptr)) { 00532 continue; 00533 } else { 00534 int integer = 0; 00535 if (sscanf(colptr, "%30d", &integer) != 1) { 00536 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00537 continue; 00538 } 00539 00540 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00541 LENGTHEN_BUF2(12); 00542 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00543 } 00544 break; 00545 case SQL_BIGINT: 00546 if (ast_strlen_zero(colptr)) { 00547 continue; 00548 } else { 00549 long long integer = 0; 00550 if (sscanf(colptr, "%30lld", &integer) != 1) { 00551 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00552 continue; 00553 } 00554 00555 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00556 LENGTHEN_BUF2(24); 00557 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer); 00558 } 00559 break; 00560 case SQL_SMALLINT: 00561 if (ast_strlen_zero(colptr)) { 00562 continue; 00563 } else { 00564 short integer = 0; 00565 if (sscanf(colptr, "%30hd", &integer) != 1) { 00566 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00567 continue; 00568 } 00569 00570 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00571 LENGTHEN_BUF2(6); 00572 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00573 } 00574 break; 00575 case SQL_TINYINT: 00576 if (ast_strlen_zero(colptr)) { 00577 continue; 00578 } else { 00579 char integer = 0; 00580 if (sscanf(colptr, "%30hhd", &integer) != 1) { 00581 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00582 continue; 00583 } 00584 00585 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00586 LENGTHEN_BUF2(4); 00587 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00588 } 00589 break; 00590 case SQL_BIT: 00591 if (ast_strlen_zero(colptr)) { 00592 continue; 00593 } else { 00594 char integer = 0; 00595 if (sscanf(colptr, "%30hhd", &integer) != 1) { 00596 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name); 00597 continue; 00598 } 00599 if (integer != 0) 00600 integer = 1; 00601 00602 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00603 LENGTHEN_BUF2(2); 00604 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); 00605 } 00606 break; 00607 case SQL_NUMERIC: 00608 case SQL_DECIMAL: 00609 if (ast_strlen_zero(colptr)) { 00610 continue; 00611 } else { 00612 double number = 0.0; 00613 00614 if (!strcasecmp(entry->cdrname, "billsec")) { 00615 if (!ast_tvzero(cdr->answer)) { 00616 snprintf(colbuf, sizeof(colbuf), "%lf", 00617 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0)); 00618 } else { 00619 ast_copy_string(colbuf, "0", sizeof(colbuf)); 00620 } 00621 } else if (!strcasecmp(entry->cdrname, "duration")) { 00622 snprintf(colbuf, sizeof(colbuf), "%lf", 00623 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0)); 00624 00625 if (!ast_strlen_zero(colbuf)) { 00626 colptr = colbuf; 00627 } 00628 } 00629 00630 if (sscanf(colptr, "%30lf", &number) != 1) { 00631 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name); 00632 continue; 00633 } 00634 00635 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00636 LENGTHEN_BUF2(entry->decimals); 00637 ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number); 00638 } 00639 break; 00640 case SQL_FLOAT: 00641 case SQL_REAL: 00642 case SQL_DOUBLE: 00643 if (ast_strlen_zero(colptr)) { 00644 continue; 00645 } else { 00646 double number = 0.0; 00647 00648 if (!strcasecmp(entry->cdrname, "billsec")) { 00649 if (!ast_tvzero(cdr->answer)) { 00650 snprintf(colbuf, sizeof(colbuf), "%lf", 00651 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0)); 00652 } else { 00653 ast_copy_string(colbuf, "0", sizeof(colbuf)); 00654 } 00655 } else if (!strcasecmp(entry->cdrname, "duration")) { 00656 snprintf(colbuf, sizeof(colbuf), "%lf", 00657 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0)); 00658 00659 if (!ast_strlen_zero(colbuf)) { 00660 colptr = colbuf; 00661 } 00662 } 00663 00664 if (sscanf(colptr, "%30lf", &number) != 1) { 00665 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name); 00666 continue; 00667 } 00668 00669 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); 00670 LENGTHEN_BUF2(entry->decimals); 00671 ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number); 00672 } 00673 break; 00674 default: 00675 ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name); 00676 continue; 00677 } 00678 first = 0; 00679 } 00680 } 00681 00682 /* Concatenate the two constructed buffers */ 00683 LENGTHEN_BUF1(ast_str_strlen(sql2)); 00684 ast_str_append(&sql, 0, ")"); 00685 ast_str_append(&sql2, 0, ")"); 00686 ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2)); 00687 00688 ast_verb(11, "[%s]\n", ast_str_buffer(sql)); 00689 00690 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql)); 00691 if (stmt) { 00692 SQLRowCount(stmt, &rows); 00693 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00694 } 00695 if (rows == 0) { 00696 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql)); 00697 } 00698 early_release: 00699 ast_odbc_release_obj(obj); 00700 } 00701 AST_RWLIST_UNLOCK(&odbc_tables); 00702 00703 /* Next time, just allocate buffers that are that big to start with. */ 00704 if (ast_str_strlen(sql) > maxsize) { 00705 maxsize = ast_str_strlen(sql); 00706 } 00707 if (ast_str_strlen(sql2) > maxsize2) { 00708 maxsize2 = ast_str_strlen(sql2); 00709 } 00710 00711 ast_free(sql); 00712 ast_free(sql2); 00713 return 0; 00714 }
static int reload | ( | void | ) | [static] |
Definition at line 743 of file cdr_adaptive_odbc.c.
References ast_log(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), load_config(), and LOG_ERROR.
00744 { 00745 if (AST_RWLIST_WRLOCK(&odbc_tables)) { 00746 ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n"); 00747 return -1; 00748 } 00749 00750 free_config(); 00751 load_config(); 00752 AST_RWLIST_UNLOCK(&odbc_tables); 00753 return 0; 00754 }
static int unload_module | ( | void | ) | [static] |
Definition at line 716 of file cdr_adaptive_odbc.c.
References ast_cdr_register(), ast_cdr_unregister(), ast_log(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), LOG_ERROR, and odbc_log().
00717 { 00718 ast_cdr_unregister(name); 00719 if (AST_RWLIST_WRLOCK(&odbc_tables)) { 00720 ast_cdr_register(name, ast_module_info->description, odbc_log); 00721 ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n"); 00722 return -1; 00723 } 00724 00725 free_config(); 00726 AST_RWLIST_UNLOCK(&odbc_tables); 00727 return 0; 00728 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Adaptive ODBC CDR backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static] |
Definition at line 761 of file cdr_adaptive_odbc.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 761 of file cdr_adaptive_odbc.c.
int maxsize = 512 [static] |
int maxsize2 = 512 [static] |
const char name[] = "Adaptive ODBC" [static] |
Definition at line 52 of file cdr_adaptive_odbc.c.
Referenced by __analog_ss_thread(), __ast_change_name_nolink(), __ast_channel_alloc_ap(), __ast_data_add_structure(), __iax2_show_peers(), _sip_show_peers(), acf_curl_helper(), action_atxfer(), action_getvar(), action_hangup(), action_originate(), action_redirect(), action_sendtext(), action_setvar(), action_status(), action_timeout(), adsi_load(), aji_cli_create_collection(), aji_cli_create_leafnode(), aji_cli_delete_pubsub_node(), aji_cli_list_pubsub_nodes(), aji_cli_purge_pubsub_nodes(), aji_test(), analog_ss_thread(), aoc_amount_str(), ast_cel_fabricate_channel_from_event(), ast_connected_line_source_parse(), ast_context_find_or_create(), ast_data_add_codecs(), ast_data_add_password(), ast_data_add_str(), ast_dsp_set_call_progress_zone(), ast_event_str_to_ie_type(), ast_getformatname_multiple(), ast_jb_read_conf(), ast_module_helper(), ast_monitor_change_fname(), ast_monitor_start(), ast_parse_caller_presentation(), ast_party_name_charset_parse(), ast_redirecting_reason_parse(), ast_rtp_lookup_mime_multiple2(), ast_setstate(), ast_str2tos(), ast_syslog_facility(), ast_syslog_priority(), build_calendar(), callerid_read(), change_monitor_action(), channel_spy(), cli_tps_ping(), cli_tps_report(), complete_trans_path_choice(), config_ldap(), count_agents_cb(), do_pause_or_unpause(), dump_ies(), dump_prov_ies(), entry_cmp_fn(), fac2str(), fax_session_tab_complete(), get_esc(), group_cmp_fn(), gtalk_ringing_ack(), handle_cli_core_show_translation(), handle_cli_osp_show(), handle_cli_status(), handle_redirect(), handle_register_message(), handle_showchan(), handle_tcptls_connection(), httpd_helper_thread(), load_module(), load_rpt_vars(), lua_func_read(), lua_get_variable(), lua_get_variable_value(), lua_set_variable(), lua_set_variable_value(), manager_mute_mixmonitor(), manager_rpt_local_nodes(), manager_rpt_status(), map_video_codec(), match_agent(), misdn_cfg_get_config_string(), misdn_cfg_get_name(), mwi_thread(), my_get_callerid(), oss_call(), oss_request(), parse_cookies(), party_id_write(), party_name_build_data(), party_name_read(), party_name_write(), peek_read(), phone_request(), phoneprov_callback(), process_echocancel(), process_opcode(), process_returncode(), register_verify(), reload_module(), rpt_call(), rpt_do_cmd(), rpt_do_dump(), rpt_do_fun(), rpt_do_fun1(), rpt_do_local_nodes(), rpt_do_lstats(), rpt_do_nodes(), rpt_do_stats(), rpt_exec(), rpt_manager_do_stats(), rpt_master(), show_config_description(), sip_prepare_socket(), sip_prune_realtime(), softhangup_exec(), start_monitor_action(), stop_monitor_action(), tps_taskprocessor_tab_complete(), unistim_new(), unload_module(), and update_call_counter().