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