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: 370183 $")
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 const 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 if (!chan) {
00369 return -1;
00370 }
00371
00372 ast_channel_lock(chan);
00373 if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00374 ast_channel_unlock(chan);
00375 return -1;
00376 }
00377
00378 oldlist = txn_store->data;
00379 AST_LIST_LOCK(oldlist);
00380 AST_LIST_TRAVERSE(oldlist, txn, list) {
00381 if (txn == tx) {
00382 txn->active = 1;
00383 active = txn;
00384 } else {
00385 txn->active = 0;
00386 }
00387 }
00388 AST_LIST_UNLOCK(oldlist);
00389 ast_channel_unlock(chan);
00390 return active ? 0 : -1;
00391 }
00392
00393 static void odbc_class_destructor(void *data)
00394 {
00395 struct odbc_class *class = data;
00396
00397
00398
00399 if (class->username) {
00400 ast_free(class->username);
00401 }
00402 if (class->password) {
00403 ast_free(class->password);
00404 }
00405 if (class->sanitysql) {
00406 ast_free(class->sanitysql);
00407 }
00408 ao2_ref(class->obj_container, -1);
00409 SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00410 }
00411
00412 static int null_hash_fn(const void *obj, const int flags)
00413 {
00414 return 0;
00415 }
00416
00417 static void odbc_obj_destructor(void *data)
00418 {
00419 struct odbc_obj *obj = data;
00420 struct odbc_class *class = obj->parent;
00421 obj->parent = NULL;
00422 odbc_obj_disconnect(obj);
00423 ast_mutex_destroy(&obj->lock);
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 ast_mutex_lock(&obj->lock);
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 ast_mutex_unlock(&obj->lock);
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 ast_mutex_lock(&obj->lock);
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 ast_mutex_unlock(&obj->lock);
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 ast_mutex_lock(&obj->lock);
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 ast_mutex_unlock(&obj->lock);
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 ast_mutex_lock(&obj->lock);
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 ast_mutex_unlock(&obj->lock);
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;
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 isolation = SQL_TXN_READ_COMMITTED;
00806 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00807 if (!strcasecmp(v->name, "pooling")) {
00808 if (ast_true(v->value))
00809 pooling = 1;
00810 } else if (!strncasecmp(v->name, "share", 5)) {
00811
00812 if (ast_false(v->value))
00813 pooling = 1;
00814 } else if (!strcasecmp(v->name, "limit")) {
00815 sscanf(v->value, "%30d", &limit);
00816 if (ast_true(v->value) && !limit) {
00817 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);
00818 limit = 1023;
00819 } else if (ast_false(v->value)) {
00820 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
00821 enabled = 0;
00822 break;
00823 }
00824 } else if (!strcasecmp(v->name, "idlecheck")) {
00825 sscanf(v->value, "%30u", &idlecheck);
00826 } else if (!strcasecmp(v->name, "enabled")) {
00827 enabled = ast_true(v->value);
00828 } else if (!strcasecmp(v->name, "pre-connect")) {
00829 preconnect = ast_true(v->value);
00830 } else if (!strcasecmp(v->name, "dsn")) {
00831 dsn = v->value;
00832 } else if (!strcasecmp(v->name, "username")) {
00833 username = v->value;
00834 } else if (!strcasecmp(v->name, "password")) {
00835 password = v->value;
00836 } else if (!strcasecmp(v->name, "sanitysql")) {
00837 sanitysql = v->value;
00838 } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00839 bse = ast_true(v->value);
00840 } else if (!strcasecmp(v->name, "connect_timeout")) {
00841 if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
00842 ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
00843 conntimeout = 10;
00844 }
00845 } else if (!strcasecmp(v->name, "negative_connection_cache")) {
00846 double dncache;
00847 if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
00848 ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
00849
00850 ncache.tv_sec = 300;
00851 ncache.tv_usec = 0;
00852 } else {
00853 ncache.tv_sec = (int)dncache;
00854 ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
00855 }
00856 } else if (!strcasecmp(v->name, "forcecommit")) {
00857 forcecommit = ast_true(v->value);
00858 } else if (!strcasecmp(v->name, "isolation")) {
00859 if ((isolation = text2isolation(v->value)) == 0) {
00860 ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00861 isolation = SQL_TXN_READ_COMMITTED;
00862 }
00863 }
00864 }
00865
00866 if (enabled && !ast_strlen_zero(dsn)) {
00867 new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00868
00869 if (!new) {
00870 res = -1;
00871 break;
00872 }
00873
00874 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00875 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00876
00877 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00878 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00879 ao2_ref(new, -1);
00880 return res;
00881 }
00882
00883 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00884
00885 if (pooling) {
00886 new->haspool = pooling;
00887 if (limit) {
00888 new->limit = limit;
00889 } else {
00890 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
00891 new->limit = 5;
00892 }
00893 }
00894
00895 new->backslash_is_escape = bse ? 1 : 0;
00896 new->forcecommit = forcecommit ? 1 : 0;
00897 new->isolation = isolation;
00898 new->idlecheck = idlecheck;
00899 new->conntimeout = conntimeout;
00900 new->negative_connection_cache = ncache;
00901
00902 if (cat)
00903 ast_copy_string(new->name, cat, sizeof(new->name));
00904 if (dsn)
00905 ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00906 if (username && !(new->username = ast_strdup(username))) {
00907 ao2_ref(new, -1);
00908 break;
00909 }
00910 if (password && !(new->password = ast_strdup(password))) {
00911 ao2_ref(new, -1);
00912 break;
00913 }
00914 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00915 ao2_ref(new, -1);
00916 break;
00917 }
00918
00919 odbc_register_class(new, preconnect);
00920 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00921 ao2_ref(new, -1);
00922 new = NULL;
00923 }
00924 }
00925 }
00926 ast_config_destroy(config);
00927 return res;
00928 }
00929
00930 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00931 {
00932 struct ao2_iterator aoi;
00933 struct odbc_class *class;
00934 struct odbc_obj *current;
00935 int length = 0;
00936 int which = 0;
00937 char *ret = NULL;
00938
00939 switch (cmd) {
00940 case CLI_INIT:
00941 e->command = "odbc show";
00942 e->usage =
00943 "Usage: odbc show [class]\n"
00944 " List settings of a particular ODBC class or,\n"
00945 " if not specified, all classes.\n";
00946 return NULL;
00947 case CLI_GENERATE:
00948 if (a->pos != 2)
00949 return NULL;
00950 length = strlen(a->word);
00951 aoi = ao2_iterator_init(class_container, 0);
00952 while ((class = ao2_iterator_next(&aoi))) {
00953 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00954 ret = ast_strdup(class->name);
00955 }
00956 ao2_ref(class, -1);
00957 if (ret) {
00958 break;
00959 }
00960 }
00961 ao2_iterator_destroy(&aoi);
00962 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00963 ret = ast_strdup("all");
00964 }
00965 return ret;
00966 }
00967
00968 ast_cli(a->fd, "\nODBC DSN Settings\n");
00969 ast_cli(a->fd, "-----------------\n\n");
00970 aoi = ao2_iterator_init(class_container, 0);
00971 while ((class = ao2_iterator_next(&aoi))) {
00972 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00973 int count = 0;
00974 char timestr[80];
00975 struct ast_tm tm;
00976
00977 ast_localtime(&class->last_negative_connect, &tm, NULL);
00978 ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
00979 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
00980 ast_cli(a->fd, " Last connection attempt: %s\n", timestr);
00981
00982 if (class->haspool) {
00983 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00984
00985 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count);
00986
00987 while ((current = ao2_iterator_next(&aoi2))) {
00988 ast_mutex_lock(¤t->lock);
00989 #ifdef DEBUG_THREADS
00990 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count,
00991 current->used ? "in use" :
00992 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00993 current->file, current->lineno, current->function);
00994 #else
00995 ast_cli(a->fd, " - Connection %d: %s\n", ++count,
00996 current->used ? "in use" :
00997 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00998 #endif
00999 ast_mutex_unlock(¤t->lock);
01000 ao2_ref(current, -1);
01001 }
01002 ao2_iterator_destroy(&aoi2);
01003 } else {
01004
01005 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01006 while ((current = ao2_iterator_next(&aoi2))) {
01007 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" :
01008 current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
01009 ao2_ref(current, -1);
01010 }
01011 ao2_iterator_destroy(&aoi2);
01012 }
01013 ast_cli(a->fd, "\n");
01014 }
01015 ao2_ref(class, -1);
01016 }
01017 ao2_iterator_destroy(&aoi);
01018
01019 return CLI_SUCCESS;
01020 }
01021
01022 static struct ast_cli_entry cli_odbc[] = {
01023 AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
01024 };
01025
01026 static int odbc_register_class(struct odbc_class *class, int preconnect)
01027 {
01028 struct odbc_obj *obj;
01029 if (class) {
01030 ao2_link(class_container, class);
01031
01032
01033 if (preconnect) {
01034
01035 obj = ast_odbc_request_obj(class->name, 0);
01036 if (obj) {
01037 ast_odbc_release_obj(obj);
01038 }
01039 }
01040
01041 return 0;
01042 } else {
01043 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
01044 return -1;
01045 }
01046 }
01047
01048 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
01049 {
01050 SQLINTEGER nativeerror=0, numfields=0;
01051 SQLSMALLINT diagbytes=0, i;
01052 unsigned char state[10], diagnostic[256];
01053
01054 ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
01055 if (tx) {
01056 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
01057 if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
01058
01059 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01060 for (i = 0; i < numfields; i++) {
01061 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01062 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01063 if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
01064
01065
01066
01067 SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01068 }
01069 if (i > 10) {
01070 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01071 break;
01072 }
01073 }
01074 }
01075
01076
01077 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01078 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01079 for (i = 0; i < numfields; i++) {
01080 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01081 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01082 if (i > 10) {
01083 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01084 break;
01085 }
01086 }
01087 }
01088 }
01089
01090 #ifdef DEBUG_THREADS
01091 obj->file[0] = '\0';
01092 obj->function[0] = '\0';
01093 obj->lineno = 0;
01094 #endif
01095
01096
01097
01098 obj->used = 0;
01099 if (obj->txf) {
01100
01101 obj->txf->obj = NULL;
01102 obj->txf = release_transaction(obj->txf);
01103 }
01104 ao2_ref(obj, -1);
01105 }
01106
01107 void ast_odbc_release_obj(struct odbc_obj *obj)
01108 {
01109 struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01110 odbc_release_obj2(obj, tx);
01111 }
01112
01113 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
01114 {
01115 return obj->parent->backslash_is_escape;
01116 }
01117
01118 static int commit_exec(struct ast_channel *chan, const char *data)
01119 {
01120 struct odbc_txn_frame *tx;
01121 SQLINTEGER nativeerror=0, numfields=0;
01122 SQLSMALLINT diagbytes=0, i;
01123 unsigned char state[10], diagnostic[256];
01124
01125 if (ast_strlen_zero(data)) {
01126 tx = find_transaction(chan, NULL, NULL, 1);
01127 } else {
01128 tx = find_transaction(chan, NULL, data, 0);
01129 }
01130
01131 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01132
01133 if (tx) {
01134 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01135 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01136 ast_str_reset(errors);
01137
01138
01139 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01140 for (i = 0; i < numfields; i++) {
01141 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01142 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01143 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01144 if (i > 10) {
01145 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01146 break;
01147 }
01148 }
01149 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01150 }
01151 }
01152 return 0;
01153 }
01154
01155 static int rollback_exec(struct ast_channel *chan, const char *data)
01156 {
01157 struct odbc_txn_frame *tx;
01158 SQLINTEGER nativeerror=0, numfields=0;
01159 SQLSMALLINT diagbytes=0, i;
01160 unsigned char state[10], diagnostic[256];
01161
01162 if (ast_strlen_zero(data)) {
01163 tx = find_transaction(chan, NULL, NULL, 1);
01164 } else {
01165 tx = find_transaction(chan, NULL, data, 0);
01166 }
01167
01168 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01169
01170 if (tx) {
01171 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01172 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01173 ast_str_reset(errors);
01174
01175
01176 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01177 for (i = 0; i < numfields; i++) {
01178 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01179 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01180 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01181 if (i > 10) {
01182 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01183 break;
01184 }
01185 }
01186 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01187 }
01188 }
01189 return 0;
01190 }
01191
01192 static int aoro2_class_cb(void *obj, void *arg, int flags)
01193 {
01194 struct odbc_class *class = obj;
01195 char *name = arg;
01196 if (!strcmp(class->name, name) && !class->delme) {
01197 return CMP_MATCH | CMP_STOP;
01198 }
01199 return 0;
01200 }
01201
01202 #define USE_TX (void *)(long)1
01203 #define NO_TX (void *)(long)2
01204 #define EOR_TX (void *)(long)3
01205
01206 static int aoro2_obj_cb(void *vobj, void *arg, int flags)
01207 {
01208 struct odbc_obj *obj = vobj;
01209 ast_mutex_lock(&obj->lock);
01210 if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01211 obj->used = 1;
01212 ast_mutex_unlock(&obj->lock);
01213 return CMP_MATCH | CMP_STOP;
01214 }
01215 ast_mutex_unlock(&obj->lock);
01216 return 0;
01217 }
01218
01219
01220
01221
01222 static int aoro2_obj_notx_cb(void *vobj, void *arg, int flags)
01223 {
01224 struct odbc_obj *obj = vobj;
01225 if (!obj->tx) {
01226 return CMP_MATCH | CMP_STOP;
01227 }
01228 return 0;
01229 }
01230
01231 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
01232 {
01233 struct odbc_obj *obj = NULL;
01234 struct odbc_class *class;
01235 SQLINTEGER nativeerror=0, numfields=0;
01236 SQLSMALLINT diagbytes=0, i;
01237 unsigned char state[10], diagnostic[256];
01238
01239 if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01240 ast_debug(1, "Class '%s' not found!\n", name);
01241 return NULL;
01242 }
01243
01244 ast_assert(ao2_ref(class, 0) > 1);
01245
01246 if (class->haspool) {
01247
01248 obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01249
01250 if (obj) {
01251 ast_assert(ao2_ref(obj, 0) > 1);
01252 }
01253 if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
01254 (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
01255 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01256 if (!obj) {
01257 class->count--;
01258 ao2_ref(class, -1);
01259 ast_debug(3, "Unable to allocate object\n");
01260 ast_atomic_fetchadd_int(&class->count, -1);
01261 return NULL;
01262 }
01263 ast_assert(ao2_ref(obj, 0) == 1);
01264 ast_mutex_init(&obj->lock);
01265
01266 obj->parent = class;
01267 class = NULL;
01268 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01269 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01270 ast_assert(ao2_ref(obj->parent, 0) > 0);
01271
01272 ast_atomic_fetchadd_int(&obj->parent->count, -1);
01273 ao2_ref(obj, -1);
01274 obj = NULL;
01275 } else {
01276 obj->used = 1;
01277 ao2_link(obj->parent->obj_container, obj);
01278 }
01279 } else {
01280
01281 if (!obj) {
01282 ast_atomic_fetchadd_int(&class->count, -1);
01283 }
01284
01285 ao2_ref(class, -1);
01286 class = NULL;
01287 }
01288
01289 if (!obj) {
01290 return NULL;
01291 }
01292
01293 ast_mutex_lock(&obj->lock);
01294
01295 if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01296
01297 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01298 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01299 for (i = 0; i < numfields; i++) {
01300 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01301 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01302 if (i > 10) {
01303 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01304 break;
01305 }
01306 }
01307 }
01308 }
01309 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01310
01311 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01312 ast_debug(1, "Object not found\n");
01313 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01314 if (!obj) {
01315 ao2_ref(class, -1);
01316 ast_debug(3, "Unable to allocate object\n");
01317 return NULL;
01318 }
01319 ast_mutex_init(&obj->lock);
01320
01321 obj->parent = class;
01322 class = NULL;
01323 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01324 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01325 ao2_ref(obj, -1);
01326 obj = NULL;
01327 } else {
01328 obj->used = 1;
01329 ao2_link(obj->parent->obj_container, obj);
01330 ast_atomic_fetchadd_int(&obj->parent->count, +1);
01331 }
01332 }
01333
01334 if (!obj) {
01335 return NULL;
01336 }
01337
01338 ast_mutex_lock(&obj->lock);
01339
01340 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01341 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01342 for (i = 0; i < numfields; i++) {
01343 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01344 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01345 if (i > 10) {
01346 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01347 break;
01348 }
01349 }
01350 }
01351 } else {
01352
01353 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_notx_cb, NO_TX))) {
01354
01355 ast_assert(ao2_ref(class, 0) > 1);
01356 ao2_ref(class, -1);
01357 class = NULL;
01358 } else {
01359
01360 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01361 ast_assert(ao2_ref(class, 0) > 1);
01362 ao2_ref(class, -1);
01363 ast_debug(3, "Unable to allocate object\n");
01364 return NULL;
01365 }
01366 ast_mutex_init(&obj->lock);
01367
01368 obj->parent = class;
01369 class = NULL;
01370 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01371 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01372 ao2_ref(obj, -1);
01373 obj = NULL;
01374 } else {
01375 ao2_link(obj->parent->obj_container, obj);
01376 ast_assert(ao2_ref(obj, 0) > 1);
01377 }
01378 }
01379
01380 if (!obj) {
01381 return NULL;
01382 }
01383
01384 ast_mutex_lock(&obj->lock);
01385
01386 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01387 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01388 for (i = 0; i < numfields; i++) {
01389 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01390 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01391 if (i > 10) {
01392 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01393 break;
01394 }
01395 }
01396 }
01397 }
01398
01399 ast_assert(obj != NULL);
01400
01401
01402 if (SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01403 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01404 for (i = 0; i < numfields; i++) {
01405 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01406 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01407 if (i > 10) {
01408 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01409 break;
01410 }
01411 }
01412 }
01413
01414 if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
01415
01416 if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
01417 odbc_obj_connect(obj);
01418 }
01419 } else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01420 ast_odbc_sanity_check(obj);
01421 } else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
01422 odbc_obj_connect(obj);
01423 }
01424
01425 #ifdef DEBUG_THREADS
01426 ast_copy_string(obj->file, file, sizeof(obj->file));
01427 ast_copy_string(obj->function, function, sizeof(obj->function));
01428 obj->lineno = lineno;
01429 #endif
01430
01431
01432 ast_mutex_unlock(&obj->lock);
01433
01434 ast_assert(class == NULL);
01435
01436 ast_assert(ao2_ref(obj, 0) > 1);
01437 return obj;
01438 }
01439
01440 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
01441 {
01442 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01443 return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01444 }
01445
01446 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
01447 {
01448 struct ast_datastore *txn_store;
01449 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01450 struct odbc_txn_frame *txn = NULL;
01451
01452 if (!chan) {
01453
01454 return NULL;
01455 }
01456
01457 ast_channel_lock(chan);
01458 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01459 oldlist = txn_store->data;
01460 } else {
01461 ast_channel_unlock(chan);
01462 return NULL;
01463 }
01464
01465 AST_LIST_LOCK(oldlist);
01466 ast_channel_unlock(chan);
01467
01468 AST_LIST_TRAVERSE(oldlist, txn, list) {
01469 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01470 AST_LIST_UNLOCK(oldlist);
01471 return txn->obj;
01472 }
01473 }
01474 AST_LIST_UNLOCK(oldlist);
01475 return NULL;
01476 }
01477
01478 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
01479 {
01480 int res;
01481 SQLINTEGER err;
01482 short int mlen;
01483 unsigned char msg[200], state[10];
01484 SQLHDBC con;
01485
01486
01487 if (!obj->con) {
01488 return ODBC_SUCCESS;
01489 }
01490
01491 con = obj->con;
01492 obj->con = NULL;
01493 res = SQLDisconnect(con);
01494
01495 if (obj->parent) {
01496 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01497 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01498 } else {
01499 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01500 }
01501 }
01502
01503 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) == SQL_SUCCESS)) {
01504 ast_log(LOG_DEBUG, "Database handle %p deallocated\n", con);
01505 } else {
01506 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01507 ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01508 }
01509
01510 obj->up = 0;
01511 return ODBC_SUCCESS;
01512 }
01513
01514 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
01515 {
01516 int res;
01517 SQLINTEGER err;
01518 short int mlen;
01519 unsigned char msg[200], state[10];
01520 #ifdef NEEDTRACE
01521 SQLINTEGER enable = 1;
01522 char *tracefile = "/tmp/odbc.trace";
01523 #endif
01524 SQLHDBC con;
01525
01526 if (obj->up) {
01527 odbc_obj_disconnect(obj);
01528 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01529 } else {
01530 ast_assert(obj->con == NULL);
01531 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01532 }
01533
01534 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
01535
01536 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01537 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01538 obj->parent->last_negative_connect = ast_tvnow();
01539 return ODBC_FAIL;
01540 }
01541 SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01542 SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01543 #ifdef NEEDTRACE
01544 SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01545 SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01546 #endif
01547
01548 res = SQLConnect(con,
01549 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01550 (SQLCHAR *) obj->parent->username, SQL_NTS,
01551 (SQLCHAR *) obj->parent->password, SQL_NTS);
01552
01553 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01554 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01555 obj->parent->last_negative_connect = ast_tvnow();
01556 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01557 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) != SQL_SUCCESS)) {
01558 SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01559 ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01560 }
01561 return ODBC_FAIL;
01562 } else {
01563 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01564 obj->up = 1;
01565 obj->last_used = ast_tvnow();
01566 }
01567
01568 obj->con = con;
01569 return ODBC_SUCCESS;
01570 }
01571
01572 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01573 {
01574 AST_DECLARE_APP_ARGS(args,
01575 AST_APP_ARG(property);
01576 AST_APP_ARG(opt);
01577 );
01578 struct odbc_txn_frame *tx;
01579
01580 AST_STANDARD_APP_ARGS(args, data);
01581 if (strcasecmp(args.property, "transaction") == 0) {
01582 if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01583 ast_copy_string(buf, tx->name, len);
01584 return 0;
01585 }
01586 } else if (strcasecmp(args.property, "isolation") == 0) {
01587 if (!ast_strlen_zero(args.opt)) {
01588 tx = find_transaction(chan, NULL, args.opt, 0);
01589 } else {
01590 tx = find_transaction(chan, NULL, NULL, 1);
01591 }
01592 if (tx) {
01593 ast_copy_string(buf, isolation2text(tx->isolation), len);
01594 return 0;
01595 }
01596 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01597 if (!ast_strlen_zero(args.opt)) {
01598 tx = find_transaction(chan, NULL, args.opt, 0);
01599 } else {
01600 tx = find_transaction(chan, NULL, NULL, 1);
01601 }
01602 if (tx) {
01603 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01604 return 0;
01605 }
01606 }
01607 return -1;
01608 }
01609
01610 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
01611 {
01612 AST_DECLARE_APP_ARGS(args,
01613 AST_APP_ARG(property);
01614 AST_APP_ARG(opt);
01615 );
01616 struct odbc_txn_frame *tx;
01617 SQLINTEGER nativeerror=0, numfields=0;
01618 SQLSMALLINT diagbytes=0, i;
01619 unsigned char state[10], diagnostic[256];
01620
01621 AST_STANDARD_APP_ARGS(args, s);
01622 if (strcasecmp(args.property, "transaction") == 0) {
01623
01624 struct odbc_obj *obj;
01625 if ((tx = find_transaction(chan, NULL, value, 0))) {
01626 mark_transaction_active(chan, tx);
01627 } else {
01628
01629 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01630 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01631 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01632 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01633 return -1;
01634 }
01635 if (!find_transaction(chan, obj, value, 0)) {
01636 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01637 return -1;
01638 }
01639 obj->tx = 1;
01640 }
01641 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01642 return 0;
01643 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01644
01645 if (ast_strlen_zero(args.opt)) {
01646 tx = find_transaction(chan, NULL, NULL, 1);
01647 } else {
01648 tx = find_transaction(chan, NULL, args.opt, 0);
01649 }
01650 if (!tx) {
01651 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01652 return -1;
01653 }
01654 if (ast_true(value)) {
01655 tx->forcecommit = 1;
01656 } else if (ast_false(value)) {
01657 tx->forcecommit = 0;
01658 } else {
01659 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01660 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01661 return -1;
01662 }
01663
01664 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01665 return 0;
01666 } else if (strcasecmp(args.property, "isolation") == 0) {
01667
01668 int isolation = text2isolation(value);
01669 if (ast_strlen_zero(args.opt)) {
01670 tx = find_transaction(chan, NULL, NULL, 1);
01671 } else {
01672 tx = find_transaction(chan, NULL, args.opt, 0);
01673 }
01674 if (!tx) {
01675 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01676 return -1;
01677 }
01678 if (isolation == 0) {
01679 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01680 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01681 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01682 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01683 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01684 for (i = 0; i < numfields; i++) {
01685 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01686 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01687 if (i > 10) {
01688 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01689 break;
01690 }
01691 }
01692 } else {
01693 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01694 tx->isolation = isolation;
01695 }
01696 return 0;
01697 } else {
01698 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01699 return -1;
01700 }
01701 }
01702
01703 static struct ast_custom_function odbc_function = {
01704 .name = "ODBC",
01705 .read = acf_transaction_read,
01706 .write = acf_transaction_write,
01707 };
01708
01709 static const char * const app_commit = "ODBC_Commit";
01710 static const char * const app_rollback = "ODBC_Rollback";
01711
01712
01713
01714
01715
01716 static int data_odbc_provider_handler(const struct ast_data_search *search,
01717 struct ast_data *root)
01718 {
01719 struct ao2_iterator aoi, aoi2;
01720 struct odbc_class *class;
01721 struct odbc_obj *current;
01722 struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection;
01723 struct ast_data *enum_node;
01724 int count;
01725
01726 aoi = ao2_iterator_init(class_container, 0);
01727 while ((class = ao2_iterator_next(&aoi))) {
01728 data_odbc_class = ast_data_add_node(root, "class");
01729 if (!data_odbc_class) {
01730 ao2_ref(class, -1);
01731 continue;
01732 }
01733
01734 ast_data_add_structure(odbc_class, data_odbc_class, class);
01735
01736 if (!ao2_container_count(class->obj_container)) {
01737 ao2_ref(class, -1);
01738 continue;
01739 }
01740
01741 data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
01742 if (!data_odbc_connections) {
01743 ao2_ref(class, -1);
01744 continue;
01745 }
01746
01747 ast_data_add_bool(data_odbc_class, "shared", !class->haspool);
01748
01749 enum_node = ast_data_add_node(data_odbc_class, "isolation");
01750 if (!enum_node) {
01751 ao2_ref(class, -1);
01752 continue;
01753 }
01754 ast_data_add_int(enum_node, "value", class->isolation);
01755 ast_data_add_str(enum_node, "text", isolation2text(class->isolation));
01756
01757 count = 0;
01758 aoi2 = ao2_iterator_init(class->obj_container, 0);
01759 while ((current = ao2_iterator_next(&aoi2))) {
01760 data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection");
01761 if (!data_odbc_connection) {
01762 ao2_ref(current, -1);
01763 continue;
01764 }
01765
01766 ast_mutex_lock(¤t->lock);
01767 ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" :
01768 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
01769 ast_data_add_bool(data_odbc_connection, "transactional", current->tx);
01770 ast_mutex_unlock(¤t->lock);
01771
01772 if (class->haspool) {
01773 ast_data_add_int(data_odbc_connection, "number", ++count);
01774 }
01775
01776 ao2_ref(current, -1);
01777 }
01778 ao2_iterator_destroy(&aoi2);
01779 ao2_ref(class, -1);
01780
01781 if (!ast_data_search_match(search, data_odbc_class)) {
01782 ast_data_remove_node(root, data_odbc_class);
01783 }
01784 }
01785 ao2_iterator_destroy(&aoi);
01786 return 0;
01787 }
01788
01789
01790
01791
01792
01793 static const struct ast_data_handler odbc_provider = {
01794 .version = AST_DATA_HANDLER_VERSION,
01795 .get = data_odbc_provider_handler
01796 };
01797
01798 static const struct ast_data_entry odbc_providers[] = {
01799 AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
01800 };
01801
01802 static int reload(void)
01803 {
01804 struct odbc_cache_tables *table;
01805 struct odbc_class *class;
01806 struct odbc_obj *current;
01807 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01808
01809
01810 while ((class = ao2_iterator_next(&aoi))) {
01811 class->delme = 1;
01812 ao2_ref(class, -1);
01813 }
01814 ao2_iterator_destroy(&aoi);
01815
01816 load_odbc_config();
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838
01839
01840
01841
01842 aoi = ao2_iterator_init(class_container, 0);
01843 while ((class = ao2_iterator_next(&aoi))) {
01844 if (class->delme) {
01845 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01846 while ((current = ao2_iterator_next(&aoi2))) {
01847 ao2_unlink(class->obj_container, current);
01848 ao2_ref(current, -1);
01849
01850
01851
01852
01853 }
01854 ao2_iterator_destroy(&aoi2);
01855 ao2_unlink(class_container, class);
01856
01857
01858
01859
01860
01861 }
01862 ao2_ref(class, -1);
01863 }
01864 ao2_iterator_destroy(&aoi);
01865
01866
01867 AST_RWLIST_WRLOCK(&odbc_tables);
01868 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01869 destroy_table_cache(table);
01870 }
01871 AST_RWLIST_UNLOCK(&odbc_tables);
01872
01873 return 0;
01874 }
01875
01876 static int unload_module(void)
01877 {
01878
01879 return -1;
01880 }
01881
01882 static int load_module(void)
01883 {
01884 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
01885 return AST_MODULE_LOAD_DECLINE;
01886 if (load_odbc_config() == -1)
01887 return AST_MODULE_LOAD_DECLINE;
01888 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
01889 ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers));
01890 ast_register_application_xml(app_commit, commit_exec);
01891 ast_register_application_xml(app_rollback, rollback_exec);
01892 ast_custom_function_register(&odbc_function);
01893 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
01894 return 0;
01895 }
01896
01897 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "ODBC resource",
01898 .load = load_module,
01899 .unload = unload_module,
01900 .reload = reload,
01901 .load_pri = AST_MODPRI_REALTIME_DEPEND,
01902 );