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