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
00037
00038
00039 #include "asterisk.h"
00040
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356917 $")
00042
00043 #include "asterisk/file.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/config.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/cli.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/res_odbc.h"
00051 #include "asterisk/time.h"
00052 #include "asterisk/astobj2.h"
00053 #include "asterisk/app.h"
00054 #include "asterisk/strings.h"
00055 #include "asterisk/threadstorage.h"
00056 #include "asterisk/data.h"
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118 struct odbc_class
00119 {
00120 AST_LIST_ENTRY(odbc_class) list;
00121 char name[80];
00122 char dsn[80];
00123 char *username;
00124 char *password;
00125 char *sanitysql;
00126 SQLHENV env;
00127 unsigned int haspool:1;
00128 unsigned int delme:1;
00129 unsigned int backslash_is_escape:1;
00130 unsigned int forcecommit:1;
00131 unsigned int isolation;
00132 unsigned int limit;
00133 int count;
00134 unsigned int idlecheck;
00135 unsigned int conntimeout;
00136
00137 struct timeval negative_connection_cache;
00138
00139 struct timeval last_negative_connect;
00140
00141 struct ao2_container *obj_container;
00142 };
00143
00144 static struct ao2_container *class_container;
00145
00146 static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
00147
00148 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
00149 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
00150 static int odbc_register_class(struct odbc_class *class, int connect);
00151 static void odbc_txn_free(void *data);
00152 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx);
00153
00154 AST_THREADSTORAGE(errors_buf);
00155
00156 static struct ast_datastore_info txn_info = {
00157 .type = "ODBC_Transaction",
00158 .destroy = odbc_txn_free,
00159 };
00160
00161 struct odbc_txn_frame {
00162 AST_LIST_ENTRY(odbc_txn_frame) list;
00163 struct ast_channel *owner;
00164 struct odbc_obj *obj;
00165
00166
00167
00168
00169
00170
00171
00172 unsigned int active:1;
00173 unsigned int forcecommit:1;
00174 unsigned int isolation;
00175 char name[0];
00176 };
00177
00178 #define DATA_EXPORT_ODBC_CLASS(MEMBER) \
00179 MEMBER(odbc_class, name, AST_DATA_STRING) \
00180 MEMBER(odbc_class, dsn, AST_DATA_STRING) \
00181 MEMBER(odbc_class, username, AST_DATA_STRING) \
00182 MEMBER(odbc_class, password, AST_DATA_PASSWORD) \
00183 MEMBER(odbc_class, limit, AST_DATA_INTEGER) \
00184 MEMBER(odbc_class, count, AST_DATA_INTEGER) \
00185 MEMBER(odbc_class, forcecommit, AST_DATA_BOOLEAN)
00186
00187 AST_DATA_STRUCTURE(odbc_class, DATA_EXPORT_ODBC_CLASS);
00188
00189 static const char *isolation2text(int iso)
00190 {
00191 if (iso == SQL_TXN_READ_COMMITTED) {
00192 return "read_committed";
00193 } else if (iso == SQL_TXN_READ_UNCOMMITTED) {
00194 return "read_uncommitted";
00195 } else if (iso == SQL_TXN_SERIALIZABLE) {
00196 return "serializable";
00197 } else if (iso == SQL_TXN_REPEATABLE_READ) {
00198 return "repeatable_read";
00199 } else {
00200 return "unknown";
00201 }
00202 }
00203
00204 static int text2isolation(const char *txt)
00205 {
00206 if (strncasecmp(txt, "read_", 5) == 0) {
00207 if (strncasecmp(txt + 5, "c", 1) == 0) {
00208 return SQL_TXN_READ_COMMITTED;
00209 } else if (strncasecmp(txt + 5, "u", 1) == 0) {
00210 return SQL_TXN_READ_UNCOMMITTED;
00211 } else {
00212 return 0;
00213 }
00214 } else if (strncasecmp(txt, "ser", 3) == 0) {
00215 return SQL_TXN_SERIALIZABLE;
00216 } else if (strncasecmp(txt, "rep", 3) == 0) {
00217 return SQL_TXN_REPEATABLE_READ;
00218 } else {
00219 return 0;
00220 }
00221 }
00222
00223 static struct odbc_txn_frame *find_transaction(struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active)
00224 {
00225 struct ast_datastore *txn_store;
00226 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00227 struct odbc_txn_frame *txn = NULL;
00228
00229 if (!chan && obj && obj->txf && obj->txf->owner) {
00230 chan = obj->txf->owner;
00231 } else if (!chan) {
00232
00233 return NULL;
00234 }
00235
00236 ast_channel_lock(chan);
00237 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00238 oldlist = txn_store->data;
00239 } else {
00240
00241 if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) {
00242 ast_log(LOG_ERROR, "Unable to allocate a new datastore. Cannot create a new transaction.\n");
00243 ast_channel_unlock(chan);
00244 return NULL;
00245 }
00246
00247 if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
00248 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Cannot create a new transaction.\n");
00249 ast_datastore_free(txn_store);
00250 ast_channel_unlock(chan);
00251 return NULL;
00252 }
00253
00254 txn_store->data = oldlist;
00255 AST_LIST_HEAD_INIT(oldlist);
00256 ast_channel_datastore_add(chan, txn_store);
00257 }
00258
00259 AST_LIST_LOCK(oldlist);
00260 ast_channel_unlock(chan);
00261
00262
00263 if (obj != NULL || active == 1) {
00264 AST_LIST_TRAVERSE(oldlist, txn, list) {
00265 if (txn->obj == obj || txn->active) {
00266 AST_LIST_UNLOCK(oldlist);
00267 return txn;
00268 }
00269 }
00270 }
00271
00272 if (name != NULL) {
00273 AST_LIST_TRAVERSE(oldlist, txn, list) {
00274 if (!strcasecmp(txn->name, name)) {
00275 AST_LIST_UNLOCK(oldlist);
00276 return txn;
00277 }
00278 }
00279 }
00280
00281
00282 if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) {
00283 struct odbc_txn_frame *otxn;
00284
00285 strcpy(txn->name, name);
00286 txn->obj = obj;
00287 txn->isolation = obj->parent->isolation;
00288 txn->forcecommit = obj->parent->forcecommit;
00289 txn->owner = chan;
00290 txn->active = 1;
00291
00292
00293 AST_LIST_TRAVERSE(oldlist, otxn, list) {
00294 otxn->active = 0;
00295 }
00296 AST_LIST_INSERT_TAIL(oldlist, txn, list);
00297
00298 obj->txf = txn;
00299 obj->tx = 1;
00300 }
00301 AST_LIST_UNLOCK(oldlist);
00302
00303 return txn;
00304 }
00305
00306 static struct odbc_txn_frame *release_transaction(struct odbc_txn_frame *tx)
00307 {
00308 if (!tx) {
00309 return NULL;
00310 }
00311
00312 ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL);
00313
00314
00315 if (tx->owner) {
00316 struct ast_datastore *txn_store;
00317 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00318
00319 ast_channel_lock(tx->owner);
00320 if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) {
00321 oldlist = txn_store->data;
00322 AST_LIST_LOCK(oldlist);
00323 AST_LIST_REMOVE(oldlist, tx, list);
00324 AST_LIST_UNLOCK(oldlist);
00325 }
00326 ast_channel_unlock(tx->owner);
00327 tx->owner = NULL;
00328 }
00329
00330 if (tx->obj) {
00331
00332 struct odbc_obj *obj = tx->obj;
00333
00334 tx->obj->txf = NULL;
00335 tx->obj = NULL;
00336 odbc_release_obj2(obj, tx);
00337 }
00338 ast_free(tx);
00339 return NULL;
00340 }
00341
00342 static void odbc_txn_free(void *vdata)
00343 {
00344 struct odbc_txn_frame *tx;
00345 AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
00346
00347 ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
00348
00349 AST_LIST_LOCK(oldlist);
00350 while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00351 release_transaction(tx);
00352 }
00353 AST_LIST_UNLOCK(oldlist);
00354 AST_LIST_HEAD_DESTROY(oldlist);
00355 ast_free(oldlist);
00356 }
00357
00358 static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
00359 {
00360 struct ast_datastore *txn_store;
00361 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00362 struct odbc_txn_frame *active = NULL, *txn;
00363
00364 if (!chan && tx && tx->owner) {
00365 chan = tx->owner;
00366 }
00367
00368 ast_channel_lock(chan);
00369 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00370 ast_channel_unlock(chan);
00371 return -1;
00372 }
00373
00374 oldlist = txn_store->data;
00375 AST_LIST_LOCK(oldlist);
00376 AST_LIST_TRAVERSE(oldlist, txn, list) {
00377 if (txn == tx) {
00378 txn->active = 1;
00379 active = txn;
00380 } else {
00381 txn->active = 0;
00382 }
00383 }
00384 AST_LIST_UNLOCK(oldlist);
00385 ast_channel_unlock(chan);
00386 return active ? 0 : -1;
00387 }
00388
00389 static void odbc_class_destructor(void *data)
00390 {
00391 struct odbc_class *class = data;
00392
00393
00394
00395 if (class->username) {
00396 ast_free(class->username);
00397 }
00398 if (class->password) {
00399 ast_free(class->password);
00400 }
00401 if (class->sanitysql) {
00402 ast_free(class->sanitysql);
00403 }
00404 ao2_ref(class->obj_container, -1);
00405 SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00406 }
00407
00408 static int null_hash_fn(const void *obj, const int flags)
00409 {
00410 return 0;
00411 }
00412
00413 static void odbc_obj_destructor(void *data)
00414 {
00415 struct odbc_obj *obj = data;
00416 struct odbc_class *class = obj->parent;
00417 obj->parent = NULL;
00418 odbc_obj_disconnect(obj);
00419 ast_mutex_destroy(&obj->lock);
00420 ao2_ref(class, -1);
00421 }
00422
00423 static void destroy_table_cache(struct odbc_cache_tables *table) {
00424 struct odbc_cache_columns *col;
00425 ast_debug(1, "Destroying table cache for %s\n", table->table);
00426 AST_RWLIST_WRLOCK(&table->columns);
00427 while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
00428 ast_free(col);
00429 }
00430 AST_RWLIST_UNLOCK(&table->columns);
00431 AST_RWLIST_HEAD_DESTROY(&table->columns);
00432 ast_free(table);
00433 }
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444 struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename)
00445 {
00446 struct odbc_cache_tables *tableptr;
00447 struct odbc_cache_columns *entry;
00448 char columnname[80];
00449 SQLLEN sqlptr;
00450 SQLHSTMT stmt = NULL;
00451 int res = 0, error = 0, try = 0;
00452 struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
00453
00454 AST_RWLIST_RDLOCK(&odbc_tables);
00455 AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
00456 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00457 break;
00458 }
00459 }
00460 if (tableptr) {
00461 AST_RWLIST_RDLOCK(&tableptr->columns);
00462 AST_RWLIST_UNLOCK(&odbc_tables);
00463 if (obj) {
00464 ast_odbc_release_obj(obj);
00465 }
00466 return tableptr;
00467 }
00468
00469 if (!obj) {
00470 ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
00471 AST_RWLIST_UNLOCK(&odbc_tables);
00472 return NULL;
00473 }
00474
00475
00476 ast_mutex_lock(&obj->lock);
00477 do {
00478 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00479 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00480 if (try == 0) {
00481 try = 1;
00482 ast_odbc_sanity_check(obj);
00483 continue;
00484 }
00485 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
00486 break;
00487 }
00488
00489 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00490 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00491 if (try == 0) {
00492 try = 1;
00493 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00494 ast_odbc_sanity_check(obj);
00495 continue;
00496 }
00497 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
00498 break;
00499 }
00500
00501 if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
00502 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
00503 break;
00504 }
00505
00506 tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00507 tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
00508 strcpy(tableptr->connection, database);
00509 strcpy(tableptr->table, tablename);
00510 AST_RWLIST_HEAD_INIT(&(tableptr->columns));
00511
00512 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00513 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00514
00515 if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
00516 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
00517 error = 1;
00518 break;
00519 }
00520 entry->name = (char *)entry + sizeof(*entry);
00521 strcpy(entry->name, columnname);
00522
00523 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00524 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00525 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00526 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00527 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00528 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00529
00530
00531
00532
00533 if (entry->octetlen == 0) {
00534 entry->octetlen = entry->size;
00535 }
00536
00537 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);
00538
00539 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00540 }
00541 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00542
00543 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00544 AST_RWLIST_RDLOCK(&(tableptr->columns));
00545 break;
00546 } while (1);
00547 ast_mutex_unlock(&obj->lock);
00548
00549 AST_RWLIST_UNLOCK(&odbc_tables);
00550
00551 if (error) {
00552 destroy_table_cache(tableptr);
00553 tableptr = NULL;
00554 }
00555 if (obj) {
00556 ast_odbc_release_obj(obj);
00557 }
00558 return tableptr;
00559 }
00560
00561 struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
00562 {
00563 struct odbc_cache_columns *col;
00564 AST_RWLIST_TRAVERSE(&table->columns, col, list) {
00565 if (strcasecmp(col->name, colname) == 0) {
00566 return col;
00567 }
00568 }
00569 return NULL;
00570 }
00571
00572 int ast_odbc_clear_cache(const char *database, const char *tablename)
00573 {
00574 struct odbc_cache_tables *tableptr;
00575
00576 AST_RWLIST_WRLOCK(&odbc_tables);
00577 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
00578 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00579 AST_LIST_REMOVE_CURRENT(list);
00580 destroy_table_cache(tableptr);
00581 break;
00582 }
00583 }
00584 AST_RWLIST_TRAVERSE_SAFE_END
00585 AST_RWLIST_UNLOCK(&odbc_tables);
00586 return tableptr ? 0 : -1;
00587 }
00588
00589 SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
00590 {
00591 int attempt;
00592 SQLHSTMT stmt;
00593
00594 ast_mutex_lock(&obj->lock);
00595
00596 for (attempt = 0; attempt < 2; attempt++) {
00597 stmt = exec_cb(obj, data);
00598
00599 if (stmt) {
00600 break;
00601 } else if (obj->tx) {
00602 ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n");
00603 break;
00604 } else if (attempt == 0) {
00605 ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn);
00606 }
00607 if (!ast_odbc_sanity_check(obj)) {
00608 break;
00609 }
00610 }
00611
00612 ast_mutex_unlock(&obj->lock);
00613
00614 return stmt;
00615 }
00616
00617 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
00618 {
00619 int res = 0, i, attempt;
00620 SQLINTEGER nativeerror=0, numfields=0;
00621 SQLSMALLINT diagbytes=0;
00622 unsigned char state[10], diagnostic[256];
00623 SQLHSTMT stmt;
00624
00625 ast_mutex_lock(&obj->lock);
00626
00627 for (attempt = 0; attempt < 2; attempt++) {
00628
00629
00630
00631
00632
00633 stmt = prepare_cb(obj, data);
00634
00635 if (stmt) {
00636 res = SQLExecute(stmt);
00637 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00638 if (res == SQL_ERROR) {
00639 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00640 for (i = 0; i < numfields; i++) {
00641 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00642 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00643 if (i > 10) {
00644 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00645 break;
00646 }
00647 }
00648 }
00649
00650 if (obj->tx) {
00651 ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n");
00652 break;
00653 } else {
00654 ast_log(LOG_WARNING, "SQL Execute error %d! Verifying connection to %s [%s]...\n", res, obj->parent->name, obj->parent->dsn);
00655 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00656 stmt = NULL;
00657
00658 obj->up = 0;
00659
00660
00661
00662
00663 if (!ast_odbc_sanity_check(obj)) {
00664 break;
00665 }
00666 continue;
00667 }
00668 } else {
00669 obj->last_used = ast_tvnow();
00670 }
00671 break;
00672 } else if (attempt == 0) {
00673 ast_odbc_sanity_check(obj);
00674 }
00675 }
00676
00677 ast_mutex_unlock(&obj->lock);
00678
00679 return stmt;
00680 }
00681
00682 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
00683 {
00684 int res = 0, i;
00685 SQLINTEGER nativeerror=0, numfields=0;
00686 SQLSMALLINT diagbytes=0;
00687 unsigned char state[10], diagnostic[256];
00688
00689 ast_mutex_lock(&obj->lock);
00690
00691 res = SQLExecute(stmt);
00692 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00693 if (res == SQL_ERROR) {
00694 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00695 for (i = 0; i < numfields; i++) {
00696 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00697 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00698 if (i > 10) {
00699 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00700 break;
00701 }
00702 }
00703 }
00704 } else {
00705 obj->last_used = ast_tvnow();
00706 }
00707
00708 ast_mutex_unlock(&obj->lock);
00709
00710 return res;
00711 }
00712
00713 SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
00714 {
00715 SQLRETURN res;
00716
00717 if (pmaxlen == 0) {
00718 if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
00719 ast_str_make_space(buf, *StrLen_or_Ind + 1);
00720 }
00721 } else if (pmaxlen > 0) {
00722 ast_str_make_space(buf, pmaxlen);
00723 }
00724 res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
00725 ast_str_update(*buf);
00726
00727 return res;
00728 }
00729
00730 int ast_odbc_sanity_check(struct odbc_obj *obj)
00731 {
00732 char *test_sql = "select 1";
00733 SQLHSTMT stmt;
00734 int res = 0;
00735
00736 if (!ast_strlen_zero(obj->parent->sanitysql))
00737 test_sql = obj->parent->sanitysql;
00738
00739 if (obj->up) {
00740 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00741 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00742 obj->up = 0;
00743 } else {
00744 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00745 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00746 obj->up = 0;
00747 } else {
00748 res = SQLExecute(stmt);
00749 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00750 obj->up = 0;
00751 }
00752 }
00753 }
00754 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00755 }
00756
00757 if (!obj->up && !obj->tx) {
00758 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00759 odbc_obj_disconnect(obj);
00760 odbc_obj_connect(obj);
00761 }
00762 return obj->up;
00763 }
00764
00765 static int load_odbc_config(void)
00766 {
00767 static char *cfg = "res_odbc.conf";
00768 struct ast_config *config;
00769 struct ast_variable *v;
00770 char *cat;
00771 const char *dsn, *username, *password, *sanitysql;
00772 int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation;
00773 struct timeval ncache = { 0, 0 };
00774 unsigned int idlecheck;
00775 int preconnect = 0, res = 0;
00776 struct ast_flags config_flags = { 0 };
00777
00778 struct odbc_class *new;
00779
00780 config = ast_config_load(cfg, config_flags);
00781 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
00782 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00783 return -1;
00784 }
00785 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00786 if (!strcasecmp(cat, "ENV")) {
00787 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00788 setenv(v->name, v->value, 1);
00789 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00790 }
00791 } else {
00792
00793 dsn = username = password = sanitysql = NULL;
00794 enabled = 1;
00795 preconnect = idlecheck = 0;
00796 pooling = 0;
00797 limit = 0;
00798 bse = 1;
00799 conntimeout = 10;
00800 forcecommit = 0;
00801 isolation = SQL_TXN_READ_COMMITTED;
00802 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00803 if (!strcasecmp(v->name, "pooling")) {
00804 if (ast_true(v->value))
00805 pooling = 1;
00806 } else if (!strncasecmp(v->name, "share", 5)) {
00807
00808 if (ast_false(v->value))
00809 pooling = 1;
00810 } else if (!strcasecmp(v->name, "limit")) {
00811 sscanf(v->value, "%30d", &limit);
00812 if (ast_true(v->value) && !limit) {
00813 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
00814 limit = 1023;
00815 } else if (ast_false(v->value)) {
00816 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
00817 enabled = 0;
00818 break;
00819 }
00820 } else if (!strcasecmp(v->name, "idlecheck")) {
00821 sscanf(v->value, "%30u", &idlecheck);
00822 } else if (!strcasecmp(v->name, "enabled")) {
00823 enabled = ast_true(v->value);
00824 } else if (!strcasecmp(v->name, "pre-connect")) {
00825 preconnect = ast_true(v->value);
00826 } else if (!strcasecmp(v->name, "dsn")) {
00827 dsn = v->value;
00828 } else if (!strcasecmp(v->name, "username")) {
00829 username = v->value;
00830 } else if (!strcasecmp(v->name, "password")) {
00831 password = v->value;
00832 } else if (!strcasecmp(v->name, "sanitysql")) {
00833 sanitysql = v->value;
00834 } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00835 bse = ast_true(v->value);
00836 } else if (!strcasecmp(v->name, "connect_timeout")) {
00837 if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
00838 ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
00839 conntimeout = 10;
00840 }
00841 } else if (!strcasecmp(v->name, "negative_connection_cache")) {
00842 double dncache;
00843 if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
00844 ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
00845
00846 ncache.tv_sec = 300;
00847 ncache.tv_usec = 0;
00848 } else {
00849 ncache.tv_sec = (int)dncache;
00850 ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
00851 }
00852 } else if (!strcasecmp(v->name, "forcecommit")) {
00853 forcecommit = ast_true(v->value);
00854 } else if (!strcasecmp(v->name, "isolation")) {
00855 if ((isolation = text2isolation(v->value)) == 0) {
00856 ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00857 isolation = SQL_TXN_READ_COMMITTED;
00858 }
00859 }
00860 }
00861
00862 if (enabled && !ast_strlen_zero(dsn)) {
00863 new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00864
00865 if (!new) {
00866 res = -1;
00867 break;
00868 }
00869
00870 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00871 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00872
00873 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00874 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00875 ao2_ref(new, -1);
00876 return res;
00877 }
00878
00879 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00880
00881 if (pooling) {
00882 new->haspool = pooling;
00883 if (limit) {
00884 new->limit = limit;
00885 } else {
00886 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
00887 new->limit = 5;
00888 }
00889 }
00890
00891 new->backslash_is_escape = bse ? 1 : 0;
00892 new->forcecommit = forcecommit ? 1 : 0;
00893 new->isolation = isolation;
00894 new->idlecheck = idlecheck;
00895 new->conntimeout = conntimeout;
00896 new->negative_connection_cache = ncache;
00897
00898 if (cat)
00899 ast_copy_string(new->name, cat, sizeof(new->name));
00900 if (dsn)
00901 ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00902 if (username && !(new->username = ast_strdup(username))) {
00903 ao2_ref(new, -1);
00904 break;
00905 }
00906 if (password && !(new->password = ast_strdup(password))) {
00907 ao2_ref(new, -1);
00908 break;
00909 }
00910 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00911 ao2_ref(new, -1);
00912 break;
00913 }
00914
00915 odbc_register_class(new, preconnect);
00916 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00917 ao2_ref(new, -1);
00918 new = NULL;
00919 }
00920 }
00921 }
00922 ast_config_destroy(config);
00923 return res;
00924 }
00925
00926 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00927 {
00928 struct ao2_iterator aoi;
00929 struct odbc_class *class;
00930 struct odbc_obj *current;
00931 int length = 0;
00932 int which = 0;
00933 char *ret = NULL;
00934
00935 switch (cmd) {
00936 case CLI_INIT:
00937 e->command = "odbc show";
00938 e->usage =
00939 "Usage: odbc show [class]\n"
00940 " List settings of a particular ODBC class or,\n"
00941 " if not specified, all classes.\n";
00942 return NULL;
00943 case CLI_GENERATE:
00944 if (a->pos != 2)
00945 return NULL;
00946 length = strlen(a->word);
00947 aoi = ao2_iterator_init(class_container, 0);
00948 while ((class = ao2_iterator_next(&aoi))) {
00949 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00950 ret = ast_strdup(class->name);
00951 }
00952 ao2_ref(class, -1);
00953 if (ret) {
00954 break;
00955 }
00956 }
00957 ao2_iterator_destroy(&aoi);
00958 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00959 ret = ast_strdup("all");
00960 }
00961 return ret;
00962 }
00963
00964 ast_cli(a->fd, "\nODBC DSN Settings\n");
00965 ast_cli(a->fd, "-----------------\n\n");
00966 aoi = ao2_iterator_init(class_container, 0);
00967 while ((class = ao2_iterator_next(&aoi))) {
00968 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00969 int count = 0;
00970 char timestr[80];
00971 struct ast_tm tm;
00972
00973 ast_localtime(&class->last_negative_connect, &tm, NULL);
00974 ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
00975 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
00976 ast_cli(a->fd, " Last connection attempt: %s\n", timestr);
00977
00978 if (class->haspool) {
00979 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00980
00981 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count);
00982
00983 while ((current = ao2_iterator_next(&aoi2))) {
00984 ast_mutex_lock(¤t->lock);
00985 #ifdef DEBUG_THREADS
00986 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count,
00987 current->used ? "in use" :
00988 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00989 current->file, current->lineno, current->function);
00990 #else
00991 ast_cli(a->fd, " - Connection %d: %s\n", ++count,
00992 current->used ? "in use" :
00993 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00994 #endif
00995 ast_mutex_unlock(¤t->lock);
00996 ao2_ref(current, -1);
00997 }
00998 ao2_iterator_destroy(&aoi2);
00999 } else {
01000
01001 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01002 while ((current = ao2_iterator_next(&aoi2))) {
01003 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" :
01004 current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
01005 ao2_ref(current, -1);
01006 }
01007 ao2_iterator_destroy(&aoi2);
01008 }
01009 ast_cli(a->fd, "\n");
01010 }
01011 ao2_ref(class, -1);
01012 }
01013 ao2_iterator_destroy(&aoi);
01014
01015 return CLI_SUCCESS;
01016 }
01017
01018 static struct ast_cli_entry cli_odbc[] = {
01019 AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
01020 };
01021
01022 static int odbc_register_class(struct odbc_class *class, int preconnect)
01023 {
01024 struct odbc_obj *obj;
01025 if (class) {
01026 ao2_link(class_container, class);
01027
01028
01029 if (preconnect) {
01030
01031 obj = ast_odbc_request_obj(class->name, 0);
01032 if (obj) {
01033 ast_odbc_release_obj(obj);
01034 }
01035 }
01036
01037 return 0;
01038 } else {
01039 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
01040 return -1;
01041 }
01042 }
01043
01044 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
01045 {
01046 SQLINTEGER nativeerror=0, numfields=0;
01047 SQLSMALLINT diagbytes=0, i;
01048 unsigned char state[10], diagnostic[256];
01049
01050 ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
01051 if (tx) {
01052 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
01053 if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
01054
01055 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01056 for (i = 0; i < numfields; i++) {
01057 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01058 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01059 if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
01060
01061
01062
01063 SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01064 }
01065 if (i > 10) {
01066 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01067 break;
01068 }
01069 }
01070 }
01071
01072
01073 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01074 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01075 for (i = 0; i < numfields; i++) {
01076 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01077 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01078 if (i > 10) {
01079 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01080 break;
01081 }
01082 }
01083 }
01084 }
01085
01086 #ifdef DEBUG_THREADS
01087 obj->file[0] = '\0';
01088 obj->function[0] = '\0';
01089 obj->lineno = 0;
01090 #endif
01091
01092
01093
01094 obj->used = 0;
01095 if (obj->txf) {
01096
01097 obj->txf->obj = NULL;
01098 obj->txf = release_transaction(obj->txf);
01099 }
01100 ao2_ref(obj, -1);
01101 }
01102
01103 void ast_odbc_release_obj(struct odbc_obj *obj)
01104 {
01105 struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01106 odbc_release_obj2(obj, tx);
01107 }
01108
01109 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
01110 {
01111 return obj->parent->backslash_is_escape;
01112 }
01113
01114 static int commit_exec(struct ast_channel *chan, const char *data)
01115 {
01116 struct odbc_txn_frame *tx;
01117 SQLINTEGER nativeerror=0, numfields=0;
01118 SQLSMALLINT diagbytes=0, i;
01119 unsigned char state[10], diagnostic[256];
01120
01121 if (ast_strlen_zero(data)) {
01122 tx = find_transaction(chan, NULL, NULL, 1);
01123 } else {
01124 tx = find_transaction(chan, NULL, data, 0);
01125 }
01126
01127 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01128
01129 if (tx) {
01130 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01131 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01132 ast_str_reset(errors);
01133
01134
01135 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01136 for (i = 0; i < numfields; i++) {
01137 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01138 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01139 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01140 if (i > 10) {
01141 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01142 break;
01143 }
01144 }
01145 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01146 }
01147 }
01148 return 0;
01149 }
01150
01151 static int rollback_exec(struct ast_channel *chan, const char *data)
01152 {
01153 struct odbc_txn_frame *tx;
01154 SQLINTEGER nativeerror=0, numfields=0;
01155 SQLSMALLINT diagbytes=0, i;
01156 unsigned char state[10], diagnostic[256];
01157
01158 if (ast_strlen_zero(data)) {
01159 tx = find_transaction(chan, NULL, NULL, 1);
01160 } else {
01161 tx = find_transaction(chan, NULL, data, 0);
01162 }
01163
01164 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01165
01166 if (tx) {
01167 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01168 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01169 ast_str_reset(errors);
01170
01171
01172 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01173 for (i = 0; i < numfields; i++) {
01174 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01175 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01176 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01177 if (i > 10) {
01178 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01179 break;
01180 }
01181 }
01182 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01183 }
01184 }
01185 return 0;
01186 }
01187
01188 static int aoro2_class_cb(void *obj, void *arg, int flags)
01189 {
01190 struct odbc_class *class = obj;
01191 char *name = arg;
01192 if (!strcmp(class->name, name) && !class->delme) {
01193 return CMP_MATCH | CMP_STOP;
01194 }
01195 return 0;
01196 }
01197
01198 #define USE_TX (void *)(long)1
01199 #define NO_TX (void *)(long)2
01200 #define EOR_TX (void *)(long)3
01201
01202 static int aoro2_obj_cb(void *vobj, void *arg, int flags)
01203 {
01204 struct odbc_obj *obj = vobj;
01205 ast_mutex_lock(&obj->lock);
01206 if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01207 obj->used = 1;
01208 ast_mutex_unlock(&obj->lock);
01209 return CMP_MATCH | CMP_STOP;
01210 }
01211 ast_mutex_unlock(&obj->lock);
01212 return 0;
01213 }
01214
01215
01216
01217
01218 static int aoro2_obj_notx_cb(void *vobj, void *arg, int flags)
01219 {
01220 struct odbc_obj *obj = vobj;
01221 if (!obj->tx) {
01222 return CMP_MATCH | CMP_STOP;
01223 }
01224 return 0;
01225 }
01226
01227 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
01228 {
01229 struct odbc_obj *obj = NULL;
01230 struct odbc_class *class;
01231 SQLINTEGER nativeerror=0, numfields=0;
01232 SQLSMALLINT diagbytes=0, i;
01233 unsigned char state[10], diagnostic[256];
01234
01235 if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01236 ast_debug(1, "Class '%s' not found!\n", name);
01237 return NULL;
01238 }
01239
01240 ast_assert(ao2_ref(class, 0) > 1);
01241
01242 if (class->haspool) {
01243
01244 obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01245
01246 if (obj) {
01247 ast_assert(ao2_ref(obj, 0) > 1);
01248 }
01249 if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
01250 (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
01251 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01252 if (!obj) {
01253 class->count--;
01254 ao2_ref(class, -1);
01255 ast_debug(3, "Unable to allocate object\n");
01256 ast_atomic_fetchadd_int(&class->count, -1);
01257 return NULL;
01258 }
01259 ast_assert(ao2_ref(obj, 0) == 1);
01260 ast_mutex_init(&obj->lock);
01261
01262 obj->parent = class;
01263 class = NULL;
01264 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01265 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01266 ast_assert(ao2_ref(obj->parent, 0) > 0);
01267
01268 ast_atomic_fetchadd_int(&obj->parent->count, -1);
01269 ao2_ref(obj, -1);
01270 obj = NULL;
01271 } else {
01272 obj->used = 1;
01273 ao2_link(obj->parent->obj_container, obj);
01274 }
01275 } else {
01276
01277 if (!obj) {
01278 ast_atomic_fetchadd_int(&class->count, -1);
01279 }
01280
01281 ao2_ref(class, -1);
01282 class = NULL;
01283 }
01284
01285 if (!obj) {
01286 return NULL;
01287 }
01288
01289 ast_mutex_lock(&obj->lock);
01290
01291 if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01292
01293 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01294 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01295 for (i = 0; i < numfields; i++) {
01296 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01297 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01298 if (i > 10) {
01299 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01300 break;
01301 }
01302 }
01303 }
01304 }
01305 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01306
01307 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01308 ast_debug(1, "Object not found\n");
01309 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01310 if (!obj) {
01311 ao2_ref(class, -1);
01312 ast_debug(3, "Unable to allocate object\n");
01313 return NULL;
01314 }
01315 ast_mutex_init(&obj->lock);
01316
01317 obj->parent = class;
01318 class = NULL;
01319 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01320 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01321 ao2_ref(obj, -1);
01322 obj = NULL;
01323 } else {
01324 obj->used = 1;
01325 ao2_link(obj->parent->obj_container, obj);
01326 ast_atomic_fetchadd_int(&obj->parent->count, +1);
01327 }
01328 }
01329
01330 if (!obj) {
01331 return NULL;
01332 }
01333
01334 ast_mutex_lock(&obj->lock);
01335
01336 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01337 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01338 for (i = 0; i < numfields; i++) {
01339 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01340 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01341 if (i > 10) {
01342 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01343 break;
01344 }
01345 }
01346 }
01347 } else {
01348
01349 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_notx_cb, NO_TX))) {
01350
01351 ast_assert(ao2_ref(class, 0) > 1);
01352 ao2_ref(class, -1);
01353 class = NULL;
01354 } else {
01355
01356 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01357 ast_assert(ao2_ref(class, 0) > 1);
01358 ao2_ref(class, -1);
01359 ast_debug(3, "Unable to allocate object\n");
01360 return NULL;
01361 }
01362 ast_mutex_init(&obj->lock);
01363
01364 obj->parent = class;
01365 class = NULL;
01366 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01367 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01368 ao2_ref(obj, -1);
01369 obj = NULL;
01370 } else {
01371 ao2_link(obj->parent->obj_container, obj);
01372 ast_assert(ao2_ref(obj, 0) > 1);
01373 }
01374 }
01375
01376 if (!obj) {
01377 return NULL;
01378 }
01379
01380 ast_mutex_lock(&obj->lock);
01381
01382 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01383 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01384 for (i = 0; i < numfields; i++) {
01385 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01386 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01387 if (i > 10) {
01388 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01389 break;
01390 }
01391 }
01392 }
01393 }
01394
01395 ast_assert(obj != NULL);
01396
01397
01398 if (SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01399 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01400 for (i = 0; i < numfields; i++) {
01401 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01402 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01403 if (i > 10) {
01404 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01405 break;
01406 }
01407 }
01408 }
01409
01410 if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
01411
01412 if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
01413 odbc_obj_connect(obj);
01414 }
01415 } else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01416 ast_odbc_sanity_check(obj);
01417 } else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
01418 odbc_obj_connect(obj);
01419 }
01420
01421 #ifdef DEBUG_THREADS
01422 ast_copy_string(obj->file, file, sizeof(obj->file));
01423 ast_copy_string(obj->function, function, sizeof(obj->function));
01424 obj->lineno = lineno;
01425 #endif
01426
01427
01428 ast_mutex_unlock(&obj->lock);
01429
01430 ast_assert(class == NULL);
01431
01432 ast_assert(ao2_ref(obj, 0) > 1);
01433 return obj;
01434 }
01435
01436 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
01437 {
01438 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01439 return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01440 }
01441
01442 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
01443 {
01444 struct ast_datastore *txn_store;
01445 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01446 struct odbc_txn_frame *txn = NULL;
01447
01448 if (!chan) {
01449
01450 return NULL;
01451 }
01452
01453 ast_channel_lock(chan);
01454 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01455 oldlist = txn_store->data;
01456 } else {
01457 ast_channel_unlock(chan);
01458 return NULL;
01459 }
01460
01461 AST_LIST_LOCK(oldlist);
01462 ast_channel_unlock(chan);
01463
01464 AST_LIST_TRAVERSE(oldlist, txn, list) {
01465 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01466 AST_LIST_UNLOCK(oldlist);
01467 return txn->obj;
01468 }
01469 }
01470 AST_LIST_UNLOCK(oldlist);
01471 return NULL;
01472 }
01473
01474 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
01475 {
01476 int res;
01477 SQLINTEGER err;
01478 short int mlen;
01479 unsigned char msg[200], state[10];
01480 SQLHDBC con;
01481
01482
01483 if (!obj->con) {
01484 return ODBC_SUCCESS;
01485 }
01486
01487 con = obj->con;
01488 obj->con = NULL;
01489 res = SQLDisconnect(con);
01490
01491 if (obj->parent) {
01492 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01493 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01494 } else {
01495 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01496 }
01497 }
01498
01499 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) == SQL_SUCCESS)) {
01500 ast_log(LOG_DEBUG, "Database handle %p deallocated\n", con);
01501 } else {
01502 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01503 ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01504 }
01505
01506 obj->up = 0;
01507 return ODBC_SUCCESS;
01508 }
01509
01510 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
01511 {
01512 int res;
01513 SQLINTEGER err;
01514 short int mlen;
01515 unsigned char msg[200], state[10];
01516 #ifdef NEEDTRACE
01517 SQLINTEGER enable = 1;
01518 char *tracefile = "/tmp/odbc.trace";
01519 #endif
01520 SQLHDBC con;
01521
01522 if (obj->up) {
01523 odbc_obj_disconnect(obj);
01524 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01525 } else {
01526 ast_assert(obj->con == NULL);
01527 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01528 }
01529
01530 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
01531
01532 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01533 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01534 obj->parent->last_negative_connect = ast_tvnow();
01535 return ODBC_FAIL;
01536 }
01537 SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01538 SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01539 #ifdef NEEDTRACE
01540 SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01541 SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01542 #endif
01543
01544 res = SQLConnect(con,
01545 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01546 (SQLCHAR *) obj->parent->username, SQL_NTS,
01547 (SQLCHAR *) obj->parent->password, SQL_NTS);
01548
01549 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01550 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01551 obj->parent->last_negative_connect = ast_tvnow();
01552 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01553 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) != SQL_SUCCESS)) {
01554 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01555 ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01556 }
01557 return ODBC_FAIL;
01558 } else {
01559 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01560 obj->up = 1;
01561 obj->last_used = ast_tvnow();
01562 }
01563
01564 obj->con = con;
01565 return ODBC_SUCCESS;
01566 }
01567
01568 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01569 {
01570 AST_DECLARE_APP_ARGS(args,
01571 AST_APP_ARG(property);
01572 AST_APP_ARG(opt);
01573 );
01574 struct odbc_txn_frame *tx;
01575
01576 AST_STANDARD_APP_ARGS(args, data);
01577 if (strcasecmp(args.property, "transaction") == 0) {
01578 if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01579 ast_copy_string(buf, tx->name, len);
01580 return 0;
01581 }
01582 } else if (strcasecmp(args.property, "isolation") == 0) {
01583 if (!ast_strlen_zero(args.opt)) {
01584 tx = find_transaction(chan, NULL, args.opt, 0);
01585 } else {
01586 tx = find_transaction(chan, NULL, NULL, 1);
01587 }
01588 if (tx) {
01589 ast_copy_string(buf, isolation2text(tx->isolation), len);
01590 return 0;
01591 }
01592 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01593 if (!ast_strlen_zero(args.opt)) {
01594 tx = find_transaction(chan, NULL, args.opt, 0);
01595 } else {
01596 tx = find_transaction(chan, NULL, NULL, 1);
01597 }
01598 if (tx) {
01599 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01600 return 0;
01601 }
01602 }
01603 return -1;
01604 }
01605
01606 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
01607 {
01608 AST_DECLARE_APP_ARGS(args,
01609 AST_APP_ARG(property);
01610 AST_APP_ARG(opt);
01611 );
01612 struct odbc_txn_frame *tx;
01613 SQLINTEGER nativeerror=0, numfields=0;
01614 SQLSMALLINT diagbytes=0, i;
01615 unsigned char state[10], diagnostic[256];
01616
01617 AST_STANDARD_APP_ARGS(args, s);
01618 if (strcasecmp(args.property, "transaction") == 0) {
01619
01620 struct odbc_obj *obj;
01621 if ((tx = find_transaction(chan, NULL, value, 0))) {
01622 mark_transaction_active(chan, tx);
01623 } else {
01624
01625 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01626 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01627 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01628 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01629 return -1;
01630 }
01631 if (!(tx = find_transaction(chan, obj, value, 0))) {
01632 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01633 return -1;
01634 }
01635 obj->tx = 1;
01636 }
01637 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01638 return 0;
01639 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01640
01641 if (ast_strlen_zero(args.opt)) {
01642 tx = find_transaction(chan, NULL, NULL, 1);
01643 } else {
01644 tx = find_transaction(chan, NULL, args.opt, 0);
01645 }
01646 if (!tx) {
01647 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01648 return -1;
01649 }
01650 if (ast_true(value)) {
01651 tx->forcecommit = 1;
01652 } else if (ast_false(value)) {
01653 tx->forcecommit = 0;
01654 } else {
01655 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01656 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01657 return -1;
01658 }
01659
01660 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01661 return 0;
01662 } else if (strcasecmp(args.property, "isolation") == 0) {
01663
01664 int isolation = text2isolation(value);
01665 if (ast_strlen_zero(args.opt)) {
01666 tx = find_transaction(chan, NULL, NULL, 1);
01667 } else {
01668 tx = find_transaction(chan, NULL, args.opt, 0);
01669 }
01670 if (!tx) {
01671 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01672 return -1;
01673 }
01674 if (isolation == 0) {
01675 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01676 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01677 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01678 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01679 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01680 for (i = 0; i < numfields; i++) {
01681 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01682 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01683 if (i > 10) {
01684 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01685 break;
01686 }
01687 }
01688 } else {
01689 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01690 tx->isolation = isolation;
01691 }
01692 return 0;
01693 } else {
01694 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01695 return -1;
01696 }
01697 }
01698
01699 static struct ast_custom_function odbc_function = {
01700 .name = "ODBC",
01701 .read = acf_transaction_read,
01702 .write = acf_transaction_write,
01703 };
01704
01705 static const char * const app_commit = "ODBC_Commit";
01706 static const char * const app_rollback = "ODBC_Rollback";
01707
01708
01709
01710
01711
01712 static int data_odbc_provider_handler(const struct ast_data_search *search,
01713 struct ast_data *root)
01714 {
01715 struct ao2_iterator aoi, aoi2;
01716 struct odbc_class *class;
01717 struct odbc_obj *current;
01718 struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection;
01719 struct ast_data *enum_node;
01720 int count;
01721
01722 aoi = ao2_iterator_init(class_container, 0);
01723 while ((class = ao2_iterator_next(&aoi))) {
01724 data_odbc_class = ast_data_add_node(root, "class");
01725 if (!data_odbc_class) {
01726 ao2_ref(class, -1);
01727 continue;
01728 }
01729
01730 ast_data_add_structure(odbc_class, data_odbc_class, class);
01731
01732 if (!ao2_container_count(class->obj_container)) {
01733 ao2_ref(class, -1);
01734 continue;
01735 }
01736
01737 data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
01738 if (!data_odbc_connections) {
01739 ao2_ref(class, -1);
01740 continue;
01741 }
01742
01743 ast_data_add_bool(data_odbc_class, "shared", !class->haspool);
01744
01745 enum_node = ast_data_add_node(data_odbc_class, "isolation");
01746 if (!enum_node) {
01747 ao2_ref(class, -1);
01748 continue;
01749 }
01750 ast_data_add_int(enum_node, "value", class->isolation);
01751 ast_data_add_str(enum_node, "text", isolation2text(class->isolation));
01752
01753 count = 0;
01754 aoi2 = ao2_iterator_init(class->obj_container, 0);
01755 while ((current = ao2_iterator_next(&aoi2))) {
01756 data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection");
01757 if (!data_odbc_connection) {
01758 ao2_ref(current, -1);
01759 continue;
01760 }
01761
01762 ast_mutex_lock(¤t->lock);
01763 ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" :
01764 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
01765 ast_data_add_bool(data_odbc_connection, "transactional", current->tx);
01766 ast_mutex_unlock(¤t->lock);
01767
01768 if (class->haspool) {
01769 ast_data_add_int(data_odbc_connection, "number", ++count);
01770 }
01771
01772 ao2_ref(current, -1);
01773 }
01774 ao2_iterator_destroy(&aoi2);
01775 ao2_ref(class, -1);
01776
01777 if (!ast_data_search_match(search, data_odbc_class)) {
01778 ast_data_remove_node(root, data_odbc_class);
01779 }
01780 }
01781 ao2_iterator_destroy(&aoi);
01782 return 0;
01783 }
01784
01785
01786
01787
01788
01789 static const struct ast_data_handler odbc_provider = {
01790 .version = AST_DATA_HANDLER_VERSION,
01791 .get = data_odbc_provider_handler
01792 };
01793
01794 static const struct ast_data_entry odbc_providers[] = {
01795 AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
01796 };
01797
01798 static int reload(void)
01799 {
01800 struct odbc_cache_tables *table;
01801 struct odbc_class *class;
01802 struct odbc_obj *current;
01803 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01804
01805
01806 while ((class = ao2_iterator_next(&aoi))) {
01807 class->delme = 1;
01808 ao2_ref(class, -1);
01809 }
01810 ao2_iterator_destroy(&aoi);
01811
01812 load_odbc_config();
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838 aoi = ao2_iterator_init(class_container, 0);
01839 while ((class = ao2_iterator_next(&aoi))) {
01840 if (class->delme) {
01841 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01842 while ((current = ao2_iterator_next(&aoi2))) {
01843 ao2_unlink(class->obj_container, current);
01844 ao2_ref(current, -1);
01845
01846
01847
01848
01849 }
01850 ao2_iterator_destroy(&aoi2);
01851 ao2_unlink(class_container, class);
01852
01853
01854
01855
01856
01857 }
01858 ao2_ref(class, -1);
01859 }
01860 ao2_iterator_destroy(&aoi);
01861
01862
01863 AST_RWLIST_WRLOCK(&odbc_tables);
01864 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01865 destroy_table_cache(table);
01866 }
01867 AST_RWLIST_UNLOCK(&odbc_tables);
01868
01869 return 0;
01870 }
01871
01872 static int unload_module(void)
01873 {
01874
01875 return -1;
01876 }
01877
01878 static int load_module(void)
01879 {
01880 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
01881 return AST_MODULE_LOAD_DECLINE;
01882 if (load_odbc_config() == -1)
01883 return AST_MODULE_LOAD_DECLINE;
01884 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
01885 ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers));
01886 ast_register_application_xml(app_commit, commit_exec);
01887 ast_register_application_xml(app_rollback, rollback_exec);
01888 ast_custom_function_register(&odbc_function);
01889 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
01890 return 0;
01891 }
01892
01893 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "ODBC resource",
01894 .load = load_module,
01895 .unload = unload_module,
01896 .reload = reload,
01897 .load_pri = AST_MODPRI_REALTIME_DEPEND,
01898 );