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