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: 222187 $")
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 {
00582 obj->up = 0;
00583 ast_log(LOG_WARNING, "SQL Exec Direct failed. Attempting a reconnect...\n");
00584
00585 odbc_obj_disconnect(obj);
00586 odbc_obj_connect(obj);
00587 }
00588 }
00589
00590 return stmt;
00591 }
00592
00593 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
00594 {
00595 int res = 0, i, attempt;
00596 SQLINTEGER nativeerror=0, numfields=0;
00597 SQLSMALLINT diagbytes=0;
00598 unsigned char state[10], diagnostic[256];
00599 SQLHSTMT stmt;
00600
00601 for (attempt = 0; attempt < 2; attempt++) {
00602
00603
00604
00605
00606
00607 stmt = prepare_cb(obj, data);
00608
00609 if (stmt) {
00610 res = SQLExecute(stmt);
00611 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00612 if (res == SQL_ERROR) {
00613 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00614 for (i = 0; i < numfields; i++) {
00615 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00616 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00617 if (i > 10) {
00618 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00619 break;
00620 }
00621 }
00622 }
00623
00624 if (obj->tx) {
00625 ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n");
00626 break;
00627 } else {
00628 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00629 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00630 stmt = NULL;
00631
00632 obj->up = 0;
00633
00634
00635
00636
00637 ast_odbc_sanity_check(obj);
00638 continue;
00639 }
00640 } else {
00641 obj->last_used = ast_tvnow();
00642 }
00643 break;
00644 } else if (attempt == 0) {
00645 ast_odbc_sanity_check(obj);
00646 }
00647 }
00648
00649 return stmt;
00650 }
00651
00652 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
00653 {
00654 int res = 0, i;
00655 SQLINTEGER nativeerror=0, numfields=0;
00656 SQLSMALLINT diagbytes=0;
00657 unsigned char state[10], diagnostic[256];
00658
00659 res = SQLExecute(stmt);
00660 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00661 if (res == SQL_ERROR) {
00662 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00663 for (i = 0; i < numfields; i++) {
00664 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00665 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00666 if (i > 10) {
00667 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00668 break;
00669 }
00670 }
00671 }
00672 } else
00673 obj->last_used = ast_tvnow();
00674
00675 return res;
00676 }
00677
00678 SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
00679 {
00680 SQLRETURN res;
00681
00682 if (pmaxlen == 0) {
00683 if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
00684 ast_str_make_space(buf, *StrLen_or_Ind + 1);
00685 }
00686 } else if (pmaxlen > 0) {
00687 ast_str_make_space(buf, pmaxlen);
00688 }
00689 res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
00690 ast_str_update(*buf);
00691
00692 return res;
00693 }
00694
00695 int ast_odbc_sanity_check(struct odbc_obj *obj)
00696 {
00697 char *test_sql = "select 1";
00698 SQLHSTMT stmt;
00699 int res = 0;
00700
00701 if (!ast_strlen_zero(obj->parent->sanitysql))
00702 test_sql = obj->parent->sanitysql;
00703
00704 if (obj->up) {
00705 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00706 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00707 obj->up = 0;
00708 } else {
00709 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00710 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00711 obj->up = 0;
00712 } else {
00713 res = SQLExecute(stmt);
00714 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00715 obj->up = 0;
00716 }
00717 }
00718 }
00719 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00720 }
00721
00722 if (!obj->up && !obj->tx) {
00723 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00724 odbc_obj_disconnect(obj);
00725 odbc_obj_connect(obj);
00726 }
00727 return obj->up;
00728 }
00729
00730 static int load_odbc_config(void)
00731 {
00732 static char *cfg = "res_odbc.conf";
00733 struct ast_config *config;
00734 struct ast_variable *v;
00735 char *cat;
00736 const char *dsn, *username, *password, *sanitysql;
00737 int enabled, pooling, limit, bse, forcecommit, isolation;
00738 unsigned int idlecheck;
00739 int preconnect = 0, res = 0;
00740 struct ast_flags config_flags = { 0 };
00741
00742 struct odbc_class *new;
00743
00744 config = ast_config_load(cfg, config_flags);
00745 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
00746 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00747 return -1;
00748 }
00749 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00750 if (!strcasecmp(cat, "ENV")) {
00751 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00752 setenv(v->name, v->value, 1);
00753 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00754 }
00755 } else {
00756
00757 dsn = username = password = sanitysql = NULL;
00758 enabled = 1;
00759 preconnect = idlecheck = 0;
00760 pooling = 0;
00761 limit = 0;
00762 bse = 1;
00763 forcecommit = 0;
00764 isolation = SQL_TXN_READ_COMMITTED;
00765 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00766 if (!strcasecmp(v->name, "pooling")) {
00767 if (ast_true(v->value))
00768 pooling = 1;
00769 } else if (!strncasecmp(v->name, "share", 5)) {
00770
00771 if (ast_false(v->value))
00772 pooling = 1;
00773 } else if (!strcasecmp(v->name, "limit")) {
00774 sscanf(v->value, "%30d", &limit);
00775 if (ast_true(v->value) && !limit) {
00776 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);
00777 limit = 1023;
00778 } else if (ast_false(v->value)) {
00779 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
00780 enabled = 0;
00781 break;
00782 }
00783 } else if (!strcasecmp(v->name, "idlecheck")) {
00784 sscanf(v->value, "%30u", &idlecheck);
00785 } else if (!strcasecmp(v->name, "enabled")) {
00786 enabled = ast_true(v->value);
00787 } else if (!strcasecmp(v->name, "pre-connect")) {
00788 preconnect = ast_true(v->value);
00789 } else if (!strcasecmp(v->name, "dsn")) {
00790 dsn = v->value;
00791 } else if (!strcasecmp(v->name, "username")) {
00792 username = v->value;
00793 } else if (!strcasecmp(v->name, "password")) {
00794 password = v->value;
00795 } else if (!strcasecmp(v->name, "sanitysql")) {
00796 sanitysql = v->value;
00797 } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00798 bse = ast_true(v->value);
00799 } else if (!strcasecmp(v->name, "forcecommit")) {
00800 forcecommit = ast_true(v->value);
00801 } else if (!strcasecmp(v->name, "isolation")) {
00802 if ((isolation = text2isolation(v->value)) == 0) {
00803 ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00804 isolation = SQL_TXN_READ_COMMITTED;
00805 }
00806 }
00807 }
00808
00809 if (enabled && !ast_strlen_zero(dsn)) {
00810 new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00811
00812 if (!new) {
00813 res = -1;
00814 break;
00815 }
00816
00817 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00818 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00819
00820 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00821 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00822 ao2_ref(new, -1);
00823 return res;
00824 }
00825
00826 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00827
00828 if (pooling) {
00829 new->haspool = pooling;
00830 if (limit) {
00831 new->limit = limit;
00832 } else {
00833 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
00834 new->limit = 5;
00835 }
00836 }
00837
00838 new->backslash_is_escape = bse ? 1 : 0;
00839 new->forcecommit = forcecommit ? 1 : 0;
00840 new->isolation = isolation;
00841 new->idlecheck = idlecheck;
00842
00843 if (cat)
00844 ast_copy_string(new->name, cat, sizeof(new->name));
00845 if (dsn)
00846 ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00847 if (username && !(new->username = ast_strdup(username))) {
00848 ao2_ref(new, -1);
00849 break;
00850 }
00851 if (password && !(new->password = ast_strdup(password))) {
00852 ao2_ref(new, -1);
00853 break;
00854 }
00855 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00856 ao2_ref(new, -1);
00857 break;
00858 }
00859
00860 odbc_register_class(new, preconnect);
00861 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00862 ao2_ref(new, -1);
00863 new = NULL;
00864 }
00865 }
00866 }
00867 ast_config_destroy(config);
00868 return res;
00869 }
00870
00871 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00872 {
00873 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00874 struct odbc_class *class;
00875 struct odbc_obj *current;
00876 int length = 0;
00877 int which = 0;
00878 char *ret = NULL;
00879
00880 switch (cmd) {
00881 case CLI_INIT:
00882 e->command = "odbc show";
00883 e->usage =
00884 "Usage: odbc show [class]\n"
00885 " List settings of a particular ODBC class or,\n"
00886 " if not specified, all classes.\n";
00887 return NULL;
00888 case CLI_GENERATE:
00889 if (a->pos != 2)
00890 return NULL;
00891 length = strlen(a->word);
00892 while ((class = ao2_iterator_next(&aoi))) {
00893 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00894 ret = ast_strdup(class->name);
00895 }
00896 ao2_ref(class, -1);
00897 if (ret) {
00898 break;
00899 }
00900 }
00901 ao2_iterator_destroy(&aoi);
00902 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00903 ret = ast_strdup("all");
00904 }
00905 return ret;
00906 }
00907
00908 ast_cli(a->fd, "\nODBC DSN Settings\n");
00909 ast_cli(a->fd, "-----------------\n\n");
00910 aoi = ao2_iterator_init(class_container, 0);
00911 while ((class = ao2_iterator_next(&aoi))) {
00912 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00913 int count = 0;
00914 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
00915
00916 if (class->haspool) {
00917 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00918
00919 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count);
00920
00921 while ((current = ao2_iterator_next(&aoi2))) {
00922 ast_mutex_lock(¤t->lock);
00923 #ifdef DEBUG_THREADS
00924 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count,
00925 current->used ? "in use" :
00926 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00927 current->file, current->lineno, current->function);
00928 #else
00929 ast_cli(a->fd, " - Connection %d: %s\n", ++count,
00930 current->used ? "in use" :
00931 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00932 #endif
00933 ast_mutex_unlock(¤t->lock);
00934 ao2_ref(current, -1);
00935 }
00936 ao2_iterator_destroy(&aoi2);
00937 } else {
00938
00939 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00940 while ((current = ao2_iterator_next(&aoi2))) {
00941 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->used ? "In use" :
00942 current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
00943 ao2_ref(current, -1);
00944 }
00945 ao2_iterator_destroy(&aoi2);
00946 }
00947 ast_cli(a->fd, "\n");
00948 }
00949 ao2_ref(class, -1);
00950 }
00951 ao2_iterator_destroy(&aoi);
00952
00953 return CLI_SUCCESS;
00954 }
00955
00956 static struct ast_cli_entry cli_odbc[] = {
00957 AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
00958 };
00959
00960 static int odbc_register_class(struct odbc_class *class, int preconnect)
00961 {
00962 struct odbc_obj *obj;
00963 if (class) {
00964 ao2_link(class_container, class);
00965
00966
00967 if (preconnect) {
00968
00969 obj = ast_odbc_request_obj(class->name, 0);
00970 if (obj) {
00971 ast_odbc_release_obj(obj);
00972 }
00973 }
00974
00975 return 0;
00976 } else {
00977 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00978 return -1;
00979 }
00980 }
00981
00982 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
00983 {
00984 SQLINTEGER nativeerror=0, numfields=0;
00985 SQLSMALLINT diagbytes=0, i;
00986 unsigned char state[10], diagnostic[256];
00987
00988 ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
00989 if (tx) {
00990 ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
00991 if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
00992
00993 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00994 for (i = 0; i < numfields; i++) {
00995 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00996 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
00997 if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
00998
00999
01000
01001 SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01002 }
01003 if (i > 10) {
01004 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01005 break;
01006 }
01007 }
01008 }
01009
01010
01011 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01012 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01013 for (i = 0; i < numfields; i++) {
01014 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01015 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01016 if (i > 10) {
01017 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01018 break;
01019 }
01020 }
01021 }
01022 }
01023
01024 #ifdef DEBUG_THREADS
01025 obj->file[0] = '\0';
01026 obj->function[0] = '\0';
01027 obj->lineno = 0;
01028 #endif
01029
01030
01031
01032 obj->used = 0;
01033 if (obj->txf) {
01034
01035 obj->txf->obj = NULL;
01036 obj->txf = release_transaction(obj->txf);
01037 }
01038 ao2_ref(obj, -1);
01039 }
01040
01041 void ast_odbc_release_obj(struct odbc_obj *obj)
01042 {
01043 struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01044 odbc_release_obj2(obj, tx);
01045 }
01046
01047 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
01048 {
01049 return obj->parent->backslash_is_escape;
01050 }
01051
01052 static int commit_exec(struct ast_channel *chan, void *data)
01053 {
01054 struct odbc_txn_frame *tx;
01055 SQLINTEGER nativeerror=0, numfields=0;
01056 SQLSMALLINT diagbytes=0, i;
01057 unsigned char state[10], diagnostic[256];
01058
01059 if (ast_strlen_zero(data)) {
01060 tx = find_transaction(chan, NULL, NULL, 1);
01061 } else {
01062 tx = find_transaction(chan, NULL, data, 0);
01063 }
01064
01065 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01066
01067 if (tx) {
01068 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01069 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01070 ast_str_reset(errors);
01071
01072
01073 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01074 for (i = 0; i < numfields; i++) {
01075 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01076 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01077 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01078 if (i > 10) {
01079 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01080 break;
01081 }
01082 }
01083 pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01084 }
01085 }
01086 return 0;
01087 }
01088
01089 static int rollback_exec(struct ast_channel *chan, void *data)
01090 {
01091 struct odbc_txn_frame *tx;
01092 SQLINTEGER nativeerror=0, numfields=0;
01093 SQLSMALLINT diagbytes=0, i;
01094 unsigned char state[10], diagnostic[256];
01095
01096 if (ast_strlen_zero(data)) {
01097 tx = find_transaction(chan, NULL, NULL, 1);
01098 } else {
01099 tx = find_transaction(chan, NULL, data, 0);
01100 }
01101
01102 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01103
01104 if (tx) {
01105 if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01106 struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01107 ast_str_reset(errors);
01108
01109
01110 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01111 for (i = 0; i < numfields; i++) {
01112 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01113 ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01114 ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01115 if (i > 10) {
01116 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01117 break;
01118 }
01119 }
01120 pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01121 }
01122 }
01123 return 0;
01124 }
01125
01126 static int aoro2_class_cb(void *obj, void *arg, int flags)
01127 {
01128 struct odbc_class *class = obj;
01129 char *name = arg;
01130 if (!strcmp(class->name, name) && !class->delme) {
01131 return CMP_MATCH | CMP_STOP;
01132 }
01133 return 0;
01134 }
01135
01136 #define USE_TX (void *)(long)1
01137 #define NO_TX (void *)(long)2
01138 #define EOR_TX (void *)(long)3
01139
01140 static int aoro2_obj_cb(void *vobj, void *arg, int flags)
01141 {
01142 struct odbc_obj *obj = vobj;
01143 ast_mutex_lock(&obj->lock);
01144 if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01145 obj->used = 1;
01146 ast_mutex_unlock(&obj->lock);
01147 return CMP_MATCH | CMP_STOP;
01148 }
01149 ast_mutex_unlock(&obj->lock);
01150 return 0;
01151 }
01152
01153 #ifdef DEBUG_THREADS
01154 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
01155 #else
01156 struct odbc_obj *ast_odbc_request_obj2(const char *name, struct ast_flags flags)
01157 #endif
01158 {
01159 struct odbc_obj *obj = NULL;
01160 struct odbc_class *class;
01161 SQLINTEGER nativeerror=0, numfields=0;
01162 SQLSMALLINT diagbytes=0, i;
01163 unsigned char state[10], diagnostic[256];
01164
01165 if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01166 return NULL;
01167 }
01168
01169 ast_assert(ao2_ref(class, 0) > 1);
01170
01171 if (class->haspool) {
01172
01173 obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01174
01175 if (obj) {
01176 ast_assert(ao2_ref(obj, 0) > 1);
01177 }
01178
01179 if (!obj && (class->count < class->limit)) {
01180 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01181 if (!obj) {
01182 ao2_ref(class, -1);
01183 return NULL;
01184 }
01185 ast_assert(ao2_ref(obj, 0) == 1);
01186 ast_mutex_init(&obj->lock);
01187
01188 obj->parent = class;
01189 class = NULL;
01190 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01191 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01192 ao2_ref(obj, -1);
01193 ast_assert(ao2_ref(class, 0) > 0);
01194 obj = NULL;
01195 } else {
01196 obj->used = 1;
01197 ao2_link(obj->parent->obj_container, obj);
01198 ast_atomic_fetchadd_int(&obj->parent->count, +1);
01199 }
01200 } else {
01201
01202 ao2_ref(class, -1);
01203 class = NULL;
01204 }
01205
01206 if (obj && ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01207
01208 if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01209 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01210 for (i = 0; i < numfields; i++) {
01211 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01212 ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01213 if (i > 10) {
01214 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01215 break;
01216 }
01217 }
01218 }
01219 }
01220 } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01221
01222 if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01223 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01224 if (!obj) {
01225 ao2_ref(class, -1);
01226 return NULL;
01227 }
01228 ast_mutex_init(&obj->lock);
01229
01230 obj->parent = class;
01231 class = NULL;
01232 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01233 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01234 ao2_ref(obj, -1);
01235 obj = NULL;
01236 } else {
01237 obj->used = 1;
01238 ao2_link(obj->parent->obj_container, obj);
01239 ast_atomic_fetchadd_int(&obj->parent->count, +1);
01240 }
01241 }
01242
01243 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01244 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01245 for (i = 0; i < numfields; i++) {
01246 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01247 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01248 if (i > 10) {
01249 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01250 break;
01251 }
01252 }
01253 }
01254 } else {
01255
01256 if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, NO_TX))) {
01257
01258 ast_assert(ao2_ref(class, 0) > 1);
01259 ao2_ref(class, -1);
01260 class = NULL;
01261 } else {
01262
01263 if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01264 ast_assert(ao2_ref(class, 0) > 1);
01265 ao2_ref(class, -1);
01266 return NULL;
01267 }
01268 ast_mutex_init(&obj->lock);
01269
01270 obj->parent = class;
01271 class = NULL;
01272 if (odbc_obj_connect(obj) == ODBC_FAIL) {
01273 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01274 ao2_ref(obj, -1);
01275 obj = NULL;
01276 } else {
01277 ao2_link(obj->parent->obj_container, obj);
01278 ast_assert(ao2_ref(obj, 0) > 1);
01279 }
01280 }
01281
01282 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01283 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01284 for (i = 0; i < numfields; i++) {
01285 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01286 ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01287 if (i > 10) {
01288 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01289 break;
01290 }
01291 }
01292 }
01293 }
01294
01295
01296 if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01297 SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01298 for (i = 0; i < numfields; i++) {
01299 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01300 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01301 if (i > 10) {
01302 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01303 break;
01304 }
01305 }
01306 }
01307
01308 if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01309 ast_odbc_sanity_check(obj);
01310 } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
01311 odbc_obj_connect(obj);
01312
01313 #ifdef DEBUG_THREADS
01314 if (obj) {
01315 ast_copy_string(obj->file, file, sizeof(obj->file));
01316 ast_copy_string(obj->function, function, sizeof(obj->function));
01317 obj->lineno = lineno;
01318 }
01319 #endif
01320 ast_assert(class == NULL);
01321
01322 if (obj) {
01323 ast_assert(ao2_ref(obj, 0) > 1);
01324 }
01325 return obj;
01326 }
01327
01328 #ifdef DEBUG_THREADS
01329 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
01330 #else
01331 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
01332 #endif
01333 {
01334 struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01335 #ifdef DEBUG_THREADS
01336 return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01337 #else
01338 return ast_odbc_request_obj2(name, flags);
01339 #endif
01340 }
01341
01342 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
01343 {
01344 struct ast_datastore *txn_store;
01345 AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01346 struct odbc_txn_frame *txn = NULL;
01347
01348 if (!chan) {
01349
01350 return NULL;
01351 }
01352
01353 ast_channel_lock(chan);
01354 if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01355 oldlist = txn_store->data;
01356 } else {
01357 ast_channel_unlock(chan);
01358 return NULL;
01359 }
01360
01361 AST_LIST_LOCK(oldlist);
01362 ast_channel_unlock(chan);
01363
01364 AST_LIST_TRAVERSE(oldlist, txn, list) {
01365 if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01366 AST_LIST_UNLOCK(oldlist);
01367 return txn->obj;
01368 }
01369 }
01370 AST_LIST_UNLOCK(oldlist);
01371 return NULL;
01372 }
01373
01374 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
01375 {
01376 int res;
01377 SQLINTEGER err;
01378 short int mlen;
01379 unsigned char msg[200], state[10];
01380
01381
01382 if (!obj->con) {
01383 return ODBC_SUCCESS;
01384 }
01385
01386 ast_mutex_lock(&obj->lock);
01387
01388 res = SQLDisconnect(obj->con);
01389
01390 if (obj->parent) {
01391 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01392 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01393 } else {
01394 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01395 }
01396 }
01397
01398 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
01399 obj->con = NULL;
01400 ast_log(LOG_DEBUG, "Database handle deallocated\n");
01401 } else {
01402 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01403 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
01404 }
01405
01406 obj->up = 0;
01407 ast_mutex_unlock(&obj->lock);
01408 return ODBC_SUCCESS;
01409 }
01410
01411 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
01412 {
01413 int res;
01414 SQLINTEGER err;
01415 short int mlen;
01416 unsigned char msg[200], state[10];
01417 #ifdef NEEDTRACE
01418 SQLINTEGER enable = 1;
01419 char *tracefile = "/tmp/odbc.trace";
01420 #endif
01421 ast_mutex_lock(&obj->lock);
01422
01423 if (obj->up) {
01424 odbc_obj_disconnect(obj);
01425 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01426 } else {
01427 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01428 }
01429
01430 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
01431
01432 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01433 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01434 ast_mutex_unlock(&obj->lock);
01435 return ODBC_FAIL;
01436 }
01437 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
01438 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
01439 #ifdef NEEDTRACE
01440 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01441 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01442 #endif
01443
01444 res = SQLConnect(obj->con,
01445 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01446 (SQLCHAR *) obj->parent->username, SQL_NTS,
01447 (SQLCHAR *) obj->parent->password, SQL_NTS);
01448
01449 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01450 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01451 ast_mutex_unlock(&obj->lock);
01452 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01453 return ODBC_FAIL;
01454 } else {
01455 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01456 obj->up = 1;
01457 obj->last_used = ast_tvnow();
01458 }
01459
01460 ast_mutex_unlock(&obj->lock);
01461 return ODBC_SUCCESS;
01462 }
01463
01464 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01465 {
01466 AST_DECLARE_APP_ARGS(args,
01467 AST_APP_ARG(property);
01468 AST_APP_ARG(opt);
01469 );
01470 struct odbc_txn_frame *tx;
01471
01472 AST_STANDARD_APP_ARGS(args, data);
01473 if (strcasecmp(args.property, "transaction") == 0) {
01474 if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01475 ast_copy_string(buf, tx->name, len);
01476 return 0;
01477 }
01478 } else if (strcasecmp(args.property, "isolation") == 0) {
01479 if (!ast_strlen_zero(args.opt)) {
01480 tx = find_transaction(chan, NULL, args.opt, 0);
01481 } else {
01482 tx = find_transaction(chan, NULL, NULL, 1);
01483 }
01484 if (tx) {
01485 ast_copy_string(buf, isolation2text(tx->isolation), len);
01486 return 0;
01487 }
01488 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01489 if (!ast_strlen_zero(args.opt)) {
01490 tx = find_transaction(chan, NULL, args.opt, 0);
01491 } else {
01492 tx = find_transaction(chan, NULL, NULL, 1);
01493 }
01494 if (tx) {
01495 ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01496 return 0;
01497 }
01498 }
01499 return -1;
01500 }
01501
01502 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
01503 {
01504 AST_DECLARE_APP_ARGS(args,
01505 AST_APP_ARG(property);
01506 AST_APP_ARG(opt);
01507 );
01508 struct odbc_txn_frame *tx;
01509 SQLINTEGER nativeerror=0, numfields=0;
01510 SQLSMALLINT diagbytes=0, i;
01511 unsigned char state[10], diagnostic[256];
01512
01513 AST_STANDARD_APP_ARGS(args, s);
01514 if (strcasecmp(args.property, "transaction") == 0) {
01515
01516 struct odbc_obj *obj;
01517 if ((tx = find_transaction(chan, NULL, value, 0))) {
01518 mark_transaction_active(chan, tx);
01519 } else {
01520
01521 struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01522 if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01523 ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01524 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01525 return -1;
01526 }
01527 if (!(tx = find_transaction(chan, obj, value, 0))) {
01528 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01529 return -1;
01530 }
01531 obj->tx = 1;
01532 }
01533 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01534 return 0;
01535 } else if (strcasecmp(args.property, "forcecommit") == 0) {
01536
01537 if (ast_strlen_zero(args.opt)) {
01538 tx = find_transaction(chan, NULL, NULL, 1);
01539 } else {
01540 tx = find_transaction(chan, NULL, args.opt, 0);
01541 }
01542 if (!tx) {
01543 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01544 return -1;
01545 }
01546 if (ast_true(value)) {
01547 tx->forcecommit = 1;
01548 } else if (ast_false(value)) {
01549 tx->forcecommit = 0;
01550 } else {
01551 ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01552 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01553 return -1;
01554 }
01555
01556 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01557 return 0;
01558 } else if (strcasecmp(args.property, "isolation") == 0) {
01559
01560 int isolation = text2isolation(value);
01561 if (ast_strlen_zero(args.opt)) {
01562 tx = find_transaction(chan, NULL, NULL, 1);
01563 } else {
01564 tx = find_transaction(chan, NULL, args.opt, 0);
01565 }
01566 if (!tx) {
01567 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01568 return -1;
01569 }
01570 if (isolation == 0) {
01571 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01572 ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01573 } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01574 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01575 SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01576 for (i = 0; i < numfields; i++) {
01577 SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01578 ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01579 if (i > 10) {
01580 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
01581 break;
01582 }
01583 }
01584 } else {
01585 pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01586 tx->isolation = isolation;
01587 }
01588 return 0;
01589 } else {
01590 ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01591 return -1;
01592 }
01593 }
01594
01595 static struct ast_custom_function odbc_function = {
01596 .name = "ODBC",
01597 .read = acf_transaction_read,
01598 .write = acf_transaction_write,
01599 };
01600
01601 static const char *app_commit = "ODBC_Commit";
01602 static const char *app_rollback = "ODBC_Rollback";
01603
01604 static int reload(void)
01605 {
01606 struct odbc_cache_tables *table;
01607 struct odbc_class *class;
01608 struct odbc_obj *current;
01609 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01610
01611
01612 while ((class = ao2_iterator_next(&aoi))) {
01613 class->delme = 1;
01614 ao2_ref(class, -1);
01615 }
01616 ao2_iterator_destroy(&aoi);
01617
01618 load_odbc_config();
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644 aoi = ao2_iterator_init(class_container, 0);
01645 while ((class = ao2_iterator_next(&aoi))) {
01646 if (class->delme) {
01647 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01648 while ((current = ao2_iterator_next(&aoi2))) {
01649 ao2_unlink(class->obj_container, current);
01650 ao2_ref(current, -1);
01651
01652
01653
01654
01655 }
01656 ao2_iterator_destroy(&aoi2);
01657 ao2_unlink(class_container, class);
01658
01659
01660
01661
01662
01663 }
01664 ao2_ref(class, -1);
01665 }
01666 ao2_iterator_destroy(&aoi);
01667
01668
01669 AST_RWLIST_WRLOCK(&odbc_tables);
01670 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01671 destroy_table_cache(table);
01672 }
01673 AST_RWLIST_UNLOCK(&odbc_tables);
01674
01675 return 0;
01676 }
01677
01678 static int unload_module(void)
01679 {
01680
01681 return -1;
01682 }
01683
01684 static int load_module(void)
01685 {
01686 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
01687 return AST_MODULE_LOAD_DECLINE;
01688 if (load_odbc_config() == -1)
01689 return AST_MODULE_LOAD_DECLINE;
01690 ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
01691 ast_register_application_xml(app_commit, commit_exec);
01692 ast_register_application_xml(app_rollback, rollback_exec);
01693 ast_custom_function_register(&odbc_function);
01694 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
01695 return 0;
01696 }
01697
01698 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
01699 .load = load_module,
01700 .unload = unload_module,
01701 .reload = reload,
01702 );