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: 307792 $")
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 ao2_ref(obj, -1);
01195 obj = NULL;
01196 ast_assert(ao2_ref(class, 0) > 0);
01197 ast_atomic_fetchadd_int(&class->count, -1);
01198 } else {
01199 obj->used = 1;
01200 ao2_link(obj->parent->obj_container, obj);
01201 }
01202 } else {
01203
01204 if (!obj) {
01205 ast_atomic_fetchadd_int(&class->count, -1);
01206 }
01207
01208 ao2_ref(class, -1);
01209 class = NULL;
01210 }
01211
01212 if (obj && ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01213
01214 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01215 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01216 for (i = 0; i < numfields; i++) {
01217 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01218 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01219 if (i > 10) {
01220 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01221 break;
01222 }
01223 }
01224 }
01225 }
01226 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01227
01228 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01229 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01230 if (!obj) {
01231 ao2_ref(class, -1);
01232 return NULL;
01233 }
01234 ast_mutex_init(&obj->lock);
01235
01236 obj->parent = class;
01237 class = NULL;
01238 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01239 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01240 ao2_ref(obj, -1);
01241 obj = NULL;
01242 } else {
01243 obj->used = 1;
01244 ao2_link(obj->parent->obj_container, obj);
01245 ast_atomic_fetchadd_int(&obj->parent->count, +1);
01246 }
01247 }
01248
01249 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01250 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01251 for (i = 0; i < numfields; i++) {
01252 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01253 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01254 if (i > 10) {
01255 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01256 break;
01257 }
01258 }
01259 }
01260 } else {
01261
01262 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, NO_TX))) {
01263
01264 ast_assert(ao2_ref(class, 0) > 1);
01265 ao2_ref(class, -1);
01266 class = NULL;
01267 } else {
01268
01269 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01270 ast_assert(ao2_ref(class, 0) > 1);
01271 ao2_ref(class, -1);
01272 return NULL;
01273 }
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 ao2_ref(obj, -1);
01281 obj = NULL;
01282 } else {
01283 ao2_link(obj->parent->obj_container, obj);
01284 ast_assert(ao2_ref(obj, 0) > 1);
01285 }
01286 }
01287
01288 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01289 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01290 for (i = 0; i < numfields; i++) {
01291 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01292 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01293 if (i > 10) {
01294 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01295 break;
01296 }
01297 }
01298 }
01299 }
01300
01301
01302 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01303 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01304 for (i = 0; i < numfields; i++) {
01305 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01306 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01307 if (i > 10) {
01308 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01309 break;
01310 }
01311 }
01312 }
01313
01314 if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01315 ast_odbc_sanity_check(obj);
01316 } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
01317 odbc_obj_connect(obj);
01318
01319 #ifdef DEBUG_THREADS
01320 if (obj) {
01321 ast_copy_string(obj->file, file, sizeof(obj->file));
01322 ast_copy_string(obj->function, function, sizeof(obj->function));
01323 obj->lineno = lineno;
01324 }
01325 #endif
01326 ast_assert(class == NULL);
01327
01328 if (obj) {
01329 ast_assert(ao2_ref(obj, 0) > 1);
01330 }
01331 return obj;
01332 }
01333
01334 #ifdef DEBUG_THREADS
01335 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
01336 #else
01337 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
01338 #endif
01339 {
01340 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01341 #ifdef DEBUG_THREADS
01342 return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01343 #else
01344 return ast_odbc_request_obj2(name, flags);
01345 #endif
01346 }
01347
01348 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
01349 {
01350 struct ast_datastore *txn_store;
01351 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01352 struct odbc_txn_frame *txn = NULL;
01353
01354 if (!chan) {
01355
01356 return NULL;
01357 }
01358
01359 ast_channel_lock(chan);
01360 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01361 oldlist = txn_store->data;
01362 } else {
01363 ast_channel_unlock(chan);
01364 return NULL;
01365 }
01366
01367 AST_LIST_LOCK(oldlist);
01368 ast_channel_unlock(chan);
01369
01370 AST_LIST_TRAVERSE(oldlist, txn, list) {
01371 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01372 AST_LIST_UNLOCK(oldlist);
01373 return txn->obj;
01374 }
01375 }
01376 AST_LIST_UNLOCK(oldlist);
01377 return NULL;
01378 }
01379
01380 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
01381 {
01382 int res;
01383 SQLINTEGER err;
01384 short int mlen;
01385 unsigned char msg[200], state[10];
01386
01387
01388 if (!obj->con) {
01389 return ODBC_SUCCESS;
01390 }
01391
01392 ast_mutex_lock(&obj->lock);
01393
01394 res = SQLDisconnect(obj->con);
01395
01396 if (obj->parent) {
01397 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01398 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01399 } else {
01400 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01401 }
01402 }
01403
01404 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
01405 obj->con = NULL;
01406 ast_log(LOG_DEBUG, "Database handle deallocated\n");
01407 } else {
01408 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01409 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
01410 }
01411
01412 obj->up = 0;
01413 ast_mutex_unlock(&obj->lock);
01414 return ODBC_SUCCESS;
01415 }
01416
01417 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
01418 {
01419 int res;
01420 SQLINTEGER err;
01421 short int mlen;
01422 unsigned char msg[200], state[10];
01423 #ifdef NEEDTRACE
01424 SQLINTEGER enable = 1;
01425 char *tracefile = "/tmp/odbc.trace";
01426 #endif
01427 ast_mutex_lock(&obj->lock);
01428
01429 if (obj->up) {
01430 odbc_obj_disconnect(obj);
01431 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01432 } else {
01433 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01434 }
01435
01436 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
01437
01438 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01439 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01440 ast_mutex_unlock(&obj->lock);
01441 return ODBC_FAIL;
01442 }
01443 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
01444 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
01445 #ifdef NEEDTRACE
01446 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01447 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01448 #endif
01449
01450 res = SQLConnect(obj->con,
01451 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01452 (SQLCHAR *) obj->parent->username, SQL_NTS,
01453 (SQLCHAR *) obj->parent->password, SQL_NTS);
01454
01455 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01456 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01457 ast_mutex_unlock(&obj->lock);
01458 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01459 return ODBC_FAIL;
01460 } else {
01461 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01462 obj->up = 1;
01463 obj->last_used = ast_tvnow();
01464 }
01465
01466 ast_mutex_unlock(&obj->lock);
01467 return ODBC_SUCCESS;
01468 }
01469
01470 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01471 {
01472 AST_DECLARE_APP_ARGS(args,
01473 AST_APP_ARG(property);
01474 AST_APP_ARG(opt);
01475 );
01476 struct odbc_txn_frame *tx;
01477
01478 AST_STANDARD_APP_ARGS(args, data);
01479 if (strcasecmp(args.property, "transaction") == 0) {
01480 if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01481 ast_copy_string(buf, tx->name, len);
01482 return 0;
01483 }
01484 } else if (strcasecmp(args.property, "isolation") == 0) {
01485 if (!ast_strlen_zero(args.opt)) {
01486 tx = find_transaction(chan, NULL, args.opt, 0);
01487 } else {
01488 tx = find_transaction(chan, NULL, NULL, 1);
01489 }
01490 if (tx) {
01491 ast_copy_string(buf, isolation2text(tx->isolation), len);
01492 return 0;
01493 }
01494 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01495 if (!ast_strlen_zero(args.opt)) {
01496 tx = find_transaction(chan, NULL, args.opt, 0);
01497 } else {
01498 tx = find_transaction(chan, NULL, NULL, 1);
01499 }
01500 if (tx) {
01501 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01502 return 0;
01503 }
01504 }
01505 return -1;
01506 }
01507
01508 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
01509 {
01510 AST_DECLARE_APP_ARGS(args,
01511 AST_APP_ARG(property);
01512 AST_APP_ARG(opt);
01513 );
01514 struct odbc_txn_frame *tx;
01515 SQLINTEGER nativeerror=0, numfields=0;
01516 SQLSMALLINT diagbytes=0, i;
01517 unsigned char state[10], diagnostic[256];
01518
01519 AST_STANDARD_APP_ARGS(args, s);
01520 if (strcasecmp(args.property, "transaction") == 0) {
01521
01522 struct odbc_obj *obj;
01523 if ((tx = find_transaction(chan, NULL, value, 0))) {
01524 mark_transaction_active(chan, tx);
01525 } else {
01526
01527 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01528 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01529 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01530 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01531 return -1;
01532 }
01533 if (!(tx = find_transaction(chan, obj, value, 0))) {
01534 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01535 return -1;
01536 }
01537 obj->tx = 1;
01538 }
01539 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01540 return 0;
01541 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01542
01543 if (ast_strlen_zero(args.opt)) {
01544 tx = find_transaction(chan, NULL, NULL, 1);
01545 } else {
01546 tx = find_transaction(chan, NULL, args.opt, 0);
01547 }
01548 if (!tx) {
01549 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01550 return -1;
01551 }
01552 if (ast_true(value)) {
01553 tx->forcecommit = 1;
01554 } else if (ast_false(value)) {
01555 tx->forcecommit = 0;
01556 } else {
01557 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01558 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01559 return -1;
01560 }
01561
01562 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01563 return 0;
01564 } else if (strcasecmp(args.property, "isolation") == 0) {
01565
01566 int isolation = text2isolation(value);
01567 if (ast_strlen_zero(args.opt)) {
01568 tx = find_transaction(chan, NULL, NULL, 1);
01569 } else {
01570 tx = find_transaction(chan, NULL, args.opt, 0);
01571 }
01572 if (!tx) {
01573 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01574 return -1;
01575 }
01576 if (isolation == 0) {
01577 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01578 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01579 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01580 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01581 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01582 for (i = 0; i < numfields; i++) {
01583 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01584 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01585 if (i > 10) {
01586 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01587 break;
01588 }
01589 }
01590 } else {
01591 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01592 tx->isolation = isolation;
01593 }
01594 return 0;
01595 } else {
01596 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01597 return -1;
01598 }
01599 }
01600
01601 static struct ast_custom_function odbc_function = {
01602 .name = "ODBC",
01603 .read = acf_transaction_read,
01604 .write = acf_transaction_write,
01605 };
01606
01607 static const char *app_commit = "ODBC_Commit";
01608 static const char *app_rollback = "ODBC_Rollback";
01609
01610 static int reload(void)
01611 {
01612 struct odbc_cache_tables *table;
01613 struct odbc_class *class;
01614 struct odbc_obj *current;
01615 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01616
01617
01618 while ((class = ao2_iterator_next(&aoi))) {
01619 class->delme = 1;
01620 ao2_ref(class, -1);
01621 }
01622 ao2_iterator_destroy(&aoi);
01623
01624 load_odbc_config();
01625
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 aoi = ao2_iterator_init(class_container, 0);
01651 while ((class = ao2_iterator_next(&aoi))) {
01652 if (class->delme) {
01653 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01654 while ((current = ao2_iterator_next(&aoi2))) {
01655 ao2_unlink(class->obj_container, current);
01656 ao2_ref(current, -1);
01657
01658
01659
01660
01661 }
01662 ao2_iterator_destroy(&aoi2);
01663 ao2_unlink(class_container, class);
01664
01665
01666
01667
01668
01669 }
01670 ao2_ref(class, -1);
01671 }
01672 ao2_iterator_destroy(&aoi);
01673
01674
01675 AST_RWLIST_WRLOCK(&odbc_tables);
01676 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01677 destroy_table_cache(table);
01678 }
01679 AST_RWLIST_UNLOCK(&odbc_tables);
01680
01681 return 0;
01682 }
01683
01684 static int unload_module(void)
01685 {
01686
01687 return -1;
01688 }
01689
01690 static int load_module(void)
01691 {
01692 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
01693 return AST_MODULE_LOAD_DECLINE;
01694 if (load_odbc_config() == -1)
01695 return AST_MODULE_LOAD_DECLINE;
01696 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
01697 ast_register_application_xml(app_commit, commit_exec);
01698 ast_register_application_xml(app_rollback, rollback_exec);
01699 ast_custom_function_register(&odbc_function);
01700 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
01701 return 0;
01702 }
01703
01704 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
01705 .load = load_module,
01706 .unload = unload_module,
01707 .reload = reload,
01708 );