Mon Mar 12 2012 21:22:16

Asterisk developer's documentation


res_odbc.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2008, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * res_odbc.c <ODBC resource manager>
00009  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief ODBC resource manager
00025  * 
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author Anthony Minessale II <anthmct@yahoo.com>
00028  * \author Tilghman Lesher <tilghman@digium.com>
00029  *
00030  * \arg See also: \ref cdr_odbc
00031  */
00032 
00033 /*** MODULEINFO
00034    <depend>generic_odbc</depend>
00035    <depend>ltdl</depend>
00036    <support_level>core</support_level>
00037  ***/
00038 
00039 #include "asterisk.h"
00040 
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00042 
00043 #include "asterisk/file.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/config.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/cli.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/res_odbc.h"
00051 #include "asterisk/time.h"
00052 #include "asterisk/astobj2.h"
00053 #include "asterisk/app.h"
00054 #include "asterisk/strings.h"
00055 #include "asterisk/threadstorage.h"
00056 #include "asterisk/data.h"
00057 
00058 /*** DOCUMENTATION
00059    <function name="ODBC" language="en_US">
00060       <synopsis>
00061          Controls ODBC transaction properties.
00062       </synopsis>
00063       <syntax>
00064          <parameter name="property" required="true">
00065             <enumlist>
00066                <enum name="transaction">
00067                   <para>Gets or sets the active transaction ID.  If set, and the transaction ID does not
00068                   exist and a <replaceable>database name</replaceable> is specified as an argument, it will be created.</para>
00069                </enum>
00070                <enum name="forcecommit">
00071                   <para>Controls whether a transaction will be automatically committed when the channel
00072                   hangs up.  Defaults to false.  If a <replaceable>transaction ID</replaceable> is specified in the optional argument,
00073                   the property will be applied to that ID, otherwise to the current active ID.</para>
00074                </enum>
00075                <enum name="isolation">
00076                   <para>Controls the data isolation on uncommitted transactions.  May be one of the
00077                   following: <literal>read_committed</literal>, <literal>read_uncommitted</literal>,
00078                   <literal>repeatable_read</literal>, or <literal>serializable</literal>.  Defaults to the
00079                   database setting in <filename>res_odbc.conf</filename> or <literal>read_committed</literal>
00080                   if not specified.  If a <replaceable>transaction ID</replaceable> is specified as an optional argument, it will be
00081                   applied to that ID, otherwise the current active ID.</para>
00082                </enum>
00083             </enumlist>
00084          </parameter>
00085          <parameter name="argument" required="false" />
00086       </syntax>
00087       <description>
00088          <para>The ODBC() function allows setting several properties to influence how a connected
00089          database processes transactions.</para>
00090       </description>
00091    </function>
00092    <application name="ODBC_Commit" language="en_US">
00093       <synopsis>
00094          Commits a currently open database transaction.
00095       </synopsis>
00096       <syntax>
00097          <parameter name="transaction ID" required="no" />
00098       </syntax>
00099       <description>
00100          <para>Commits the database transaction specified by <replaceable>transaction ID</replaceable>
00101          or the current active transaction, if not specified.</para>
00102       </description>
00103    </application>
00104    <application name="ODBC_Rollback" language="en_US">
00105       <synopsis>
00106          Rollback a currently open database transaction.
00107       </synopsis>
00108       <syntax>
00109          <parameter name="transaction ID" required="no" />
00110       </syntax>
00111       <description>
00112          <para>Rolls back the database transaction specified by <replaceable>transaction ID</replaceable>
00113          or the current active transaction, if not specified.</para>
00114       </description>
00115    </application>
00116  ***/
00117 
00118 struct odbc_class
00119 {
00120    AST_LIST_ENTRY(odbc_class) list;
00121    char name[80];
00122    char dsn[80];
00123    char *username;
00124    char *password;
00125    char *sanitysql;
00126    SQLHENV env;
00127    unsigned int haspool:1;              /*!< Boolean - TDS databases need this */
00128    unsigned int delme:1;                /*!< Purge the class */
00129    unsigned int backslash_is_escape:1;  /*!< On this database, the backslash is a native escape sequence */
00130    unsigned int forcecommit:1;          /*!< Should uncommitted transactions be auto-committed on handle release? */
00131    unsigned int isolation;              /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
00132    unsigned int limit;                  /*!< Maximum number of database handles we will allow */
00133    int count;                           /*!< Running count of pooled connections */
00134    unsigned int idlecheck;              /*!< Recheck the connection if it is idle for this long (in seconds) */
00135    unsigned int conntimeout;            /*!< Maximum time the connection process should take */
00136    /*! When a connection fails, cache that failure for how long? */
00137    struct timeval negative_connection_cache;
00138    /*! When a connection fails, when did that last occur? */
00139    struct timeval last_negative_connect;
00140    /*! List of handles associated with this class */
00141    struct ao2_container *obj_container;
00142 };
00143 
00144 static struct ao2_container *class_container;
00145 
00146 static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
00147 
00148 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
00149 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
00150 static int odbc_register_class(struct odbc_class *class, int connect);
00151 static void odbc_txn_free(void *data);
00152 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx);
00153 
00154 AST_THREADSTORAGE(errors_buf);
00155 
00156 static struct ast_datastore_info txn_info = {
00157    .type = "ODBC_Transaction",
00158    .destroy = odbc_txn_free,
00159 };
00160 
00161 struct odbc_txn_frame {
00162    AST_LIST_ENTRY(odbc_txn_frame) list;
00163    struct ast_channel *owner;
00164    struct odbc_obj *obj;        /*!< Database handle within which transacted statements are run */
00165    /*!\brief Is this record the current active transaction within the channel?
00166     * Note that the active flag is really only necessary for statements which
00167     * are triggered from the dialplan, as there isn't a direct correlation
00168     * between multiple statements.  Applications wishing to use transactions
00169     * may simply perform each statement on the same odbc_obj, which keeps the
00170     * transaction persistent.
00171     */
00172    unsigned int active:1;
00173    unsigned int forcecommit:1;     /*!< Should uncommitted transactions be auto-committed on handle release? */
00174    unsigned int isolation;         /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
00175    char name[0];                   /*!< Name of this transaction ID */
00176 };
00177 
00178 #define DATA_EXPORT_ODBC_CLASS(MEMBER)          \
00179    MEMBER(odbc_class, name, AST_DATA_STRING)    \
00180    MEMBER(odbc_class, dsn, AST_DATA_STRING)     \
00181    MEMBER(odbc_class, username, AST_DATA_STRING)      \
00182    MEMBER(odbc_class, password, AST_DATA_PASSWORD)    \
00183    MEMBER(odbc_class, limit, AST_DATA_INTEGER)     \
00184    MEMBER(odbc_class, count, AST_DATA_INTEGER)     \
00185    MEMBER(odbc_class, forcecommit, AST_DATA_BOOLEAN)
00186 
00187 AST_DATA_STRUCTURE(odbc_class, DATA_EXPORT_ODBC_CLASS);
00188 
00189 static const char *isolation2text(int iso)
00190 {
00191    if (iso == SQL_TXN_READ_COMMITTED) {
00192       return "read_committed";
00193    } else if (iso == SQL_TXN_READ_UNCOMMITTED) {
00194       return "read_uncommitted";
00195    } else if (iso == SQL_TXN_SERIALIZABLE) {
00196       return "serializable";
00197    } else if (iso == SQL_TXN_REPEATABLE_READ) {
00198       return "repeatable_read";
00199    } else {
00200       return "unknown";
00201    }
00202 }
00203 
00204 static int text2isolation(const char *txt)
00205 {
00206    if (strncasecmp(txt, "read_", 5) == 0) {
00207       if (strncasecmp(txt + 5, "c", 1) == 0) {
00208          return SQL_TXN_READ_COMMITTED;
00209       } else if (strncasecmp(txt + 5, "u", 1) == 0) {
00210          return SQL_TXN_READ_UNCOMMITTED;
00211       } else {
00212          return 0;
00213       }
00214    } else if (strncasecmp(txt, "ser", 3) == 0) {
00215       return SQL_TXN_SERIALIZABLE;
00216    } else if (strncasecmp(txt, "rep", 3) == 0) {
00217       return SQL_TXN_REPEATABLE_READ;
00218    } else {
00219       return 0;
00220    }
00221 }
00222 
00223 static struct odbc_txn_frame *find_transaction(struct ast_channel *chan, struct odbc_obj *obj, const char *name, int active)
00224 {
00225    struct ast_datastore *txn_store;
00226    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00227    struct odbc_txn_frame *txn = NULL;
00228 
00229    if (!chan && obj && obj->txf && obj->txf->owner) {
00230       chan = obj->txf->owner;
00231    } else if (!chan) {
00232       /* No channel == no transaction */
00233       return NULL;
00234    }
00235 
00236    ast_channel_lock(chan);
00237    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00238       oldlist = txn_store->data;
00239    } else {
00240       /* Need to create a new datastore */
00241       if (!(txn_store = ast_datastore_alloc(&txn_info, NULL))) {
00242          ast_log(LOG_ERROR, "Unable to allocate a new datastore.  Cannot create a new transaction.\n");
00243          ast_channel_unlock(chan);
00244          return NULL;
00245       }
00246 
00247       if (!(oldlist = ast_calloc(1, sizeof(*oldlist)))) {
00248          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  Cannot create a new transaction.\n");
00249          ast_datastore_free(txn_store);
00250          ast_channel_unlock(chan);
00251          return NULL;
00252       }
00253 
00254       txn_store->data = oldlist;
00255       AST_LIST_HEAD_INIT(oldlist);
00256       ast_channel_datastore_add(chan, txn_store);
00257    }
00258 
00259    AST_LIST_LOCK(oldlist);
00260    ast_channel_unlock(chan);
00261 
00262    /* Scanning for an object is *fast*.  Scanning for a name is much slower. */
00263    if (obj != NULL || active == 1) {
00264       AST_LIST_TRAVERSE(oldlist, txn, list) {
00265          if (txn->obj == obj || txn->active) {
00266             AST_LIST_UNLOCK(oldlist);
00267             return txn;
00268          }
00269       }
00270    }
00271 
00272    if (name != NULL) {
00273       AST_LIST_TRAVERSE(oldlist, txn, list) {
00274          if (!strcasecmp(txn->name, name)) {
00275             AST_LIST_UNLOCK(oldlist);
00276             return txn;
00277          }
00278       }
00279    }
00280 
00281    /* Nothing found, create one */
00282    if (name && obj && (txn = ast_calloc(1, sizeof(*txn) + strlen(name) + 1))) {
00283       struct odbc_txn_frame *otxn;
00284 
00285       strcpy(txn->name, name); /* SAFE */
00286       txn->obj = obj;
00287       txn->isolation = obj->parent->isolation;
00288       txn->forcecommit = obj->parent->forcecommit;
00289       txn->owner = chan;
00290       txn->active = 1;
00291 
00292       /* On creation, the txn becomes active, and all others inactive */
00293       AST_LIST_TRAVERSE(oldlist, otxn, list) {
00294          otxn->active = 0;
00295       }
00296       AST_LIST_INSERT_TAIL(oldlist, txn, list);
00297 
00298       obj->txf = txn;
00299       obj->tx = 1;
00300    }
00301    AST_LIST_UNLOCK(oldlist);
00302 
00303    return txn;
00304 }
00305 
00306 static struct odbc_txn_frame *release_transaction(struct odbc_txn_frame *tx)
00307 {
00308    if (!tx) {
00309       return NULL;
00310    }
00311 
00312    ast_debug(2, "release_transaction(%p) called (tx->obj = %p, tx->obj->txf = %p)\n", tx, tx->obj, tx->obj ? tx->obj->txf : NULL);
00313 
00314    /* If we have an owner, disassociate */
00315    if (tx->owner) {
00316       struct ast_datastore *txn_store;
00317       AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00318 
00319       ast_channel_lock(tx->owner);
00320       if ((txn_store = ast_channel_datastore_find(tx->owner, &txn_info, NULL))) {
00321          oldlist = txn_store->data;
00322          AST_LIST_LOCK(oldlist);
00323          AST_LIST_REMOVE(oldlist, tx, list);
00324          AST_LIST_UNLOCK(oldlist);
00325       }
00326       ast_channel_unlock(tx->owner);
00327       tx->owner = NULL;
00328    }
00329 
00330    if (tx->obj) {
00331       /* If we have any uncommitted transactions, they are handled when we release the object */
00332       struct odbc_obj *obj = tx->obj;
00333       /* Prevent recursion during destruction */
00334       tx->obj->txf = NULL;
00335       tx->obj = NULL;
00336       odbc_release_obj2(obj, tx);
00337    }
00338    ast_free(tx);
00339    return NULL;
00340 }
00341 
00342 static void odbc_txn_free(void *vdata)
00343 {
00344    struct odbc_txn_frame *tx;
00345    AST_LIST_HEAD(, odbc_txn_frame) *oldlist = vdata;
00346 
00347    ast_debug(2, "odbc_txn_free(%p) called\n", vdata);
00348 
00349    AST_LIST_LOCK(oldlist);
00350    while ((tx = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00351       release_transaction(tx);
00352    }
00353    AST_LIST_UNLOCK(oldlist);
00354    AST_LIST_HEAD_DESTROY(oldlist);
00355    ast_free(oldlist);
00356 }
00357 
00358 static int mark_transaction_active(struct ast_channel *chan, struct odbc_txn_frame *tx)
00359 {
00360    struct ast_datastore *txn_store;
00361    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
00362    struct odbc_txn_frame *active = NULL, *txn;
00363 
00364    if (!chan && tx && tx->owner) {
00365       chan = tx->owner;
00366    }
00367 
00368    ast_channel_lock(chan);
00369    if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00370       ast_channel_unlock(chan);
00371       return -1;
00372    }
00373 
00374    oldlist = txn_store->data;
00375    AST_LIST_LOCK(oldlist);
00376    AST_LIST_TRAVERSE(oldlist, txn, list) {
00377       if (txn == tx) {
00378          txn->active = 1;
00379          active = txn;
00380       } else {
00381          txn->active = 0;
00382       }
00383    }
00384    AST_LIST_UNLOCK(oldlist);
00385    ast_channel_unlock(chan);
00386    return active ? 0 : -1;
00387 }
00388 
00389 static void odbc_class_destructor(void *data)
00390 {
00391    struct odbc_class *class = data;
00392    /* Due to refcounts, we can safely assume that any objects with a reference
00393     * to us will prevent our destruction, so we don't need to worry about them.
00394     */
00395    if (class->username) {
00396       ast_free(class->username);
00397    }
00398    if (class->password) {
00399       ast_free(class->password);
00400    }
00401    if (class->sanitysql) {
00402       ast_free(class->sanitysql);
00403    }
00404    ao2_ref(class->obj_container, -1);
00405    SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00406 }
00407 
00408 static int null_hash_fn(const void *obj, const int flags)
00409 {
00410    return 0;
00411 }
00412 
00413 static void odbc_obj_destructor(void *data)
00414 {
00415    struct odbc_obj *obj = data;
00416    struct odbc_class *class = obj->parent;
00417    obj->parent = NULL;
00418    odbc_obj_disconnect(obj);
00419    ast_mutex_destroy(&obj->lock);
00420    ao2_ref(class, -1);
00421 }
00422 
00423 static void destroy_table_cache(struct odbc_cache_tables *table) {
00424    struct odbc_cache_columns *col;
00425    ast_debug(1, "Destroying table cache for %s\n", table->table);
00426    AST_RWLIST_WRLOCK(&table->columns);
00427    while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
00428       ast_free(col);
00429    }
00430    AST_RWLIST_UNLOCK(&table->columns);
00431    AST_RWLIST_HEAD_DESTROY(&table->columns);
00432    ast_free(table);
00433 }
00434 
00435 /*!
00436  * \brief Find or create an entry describing the table specified.
00437  * \param database Name of an ODBC class on which to query the table
00438  * \param tablename Tablename to describe
00439  * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
00440  * When a structure is returned, the contained columns list will be
00441  * rdlock'ed, to ensure that it will be retained in memory.
00442  * \since 1.6.1
00443  */
00444 struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename)
00445 {
00446    struct odbc_cache_tables *tableptr;
00447    struct odbc_cache_columns *entry;
00448    char columnname[80];
00449    SQLLEN sqlptr;
00450    SQLHSTMT stmt = NULL;
00451    int res = 0, error = 0, try = 0;
00452    struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
00453 
00454    AST_RWLIST_RDLOCK(&odbc_tables);
00455    AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
00456       if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00457          break;
00458       }
00459    }
00460    if (tableptr) {
00461       AST_RWLIST_RDLOCK(&tableptr->columns);
00462       AST_RWLIST_UNLOCK(&odbc_tables);
00463       if (obj) {
00464          ast_odbc_release_obj(obj);
00465       }
00466       return tableptr;
00467    }
00468 
00469    if (!obj) {
00470       ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
00471       AST_RWLIST_UNLOCK(&odbc_tables);
00472       return NULL;
00473    }
00474 
00475    /* Table structure not already cached; build it now. */
00476    do {
00477       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00478       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00479          if (try == 0) {
00480             try = 1;
00481             ast_odbc_sanity_check(obj);
00482             continue;
00483          }
00484          ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
00485          break;
00486       }
00487 
00488       res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00489       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00490          if (try == 0) {
00491             try = 1;
00492             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00493             ast_odbc_sanity_check(obj);
00494             continue;
00495          }
00496          ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
00497          break;
00498       }
00499 
00500       if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
00501          ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
00502          break;
00503       }
00504 
00505       tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00506       tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
00507       strcpy(tableptr->connection, database); /* SAFE */
00508       strcpy(tableptr->table, tablename); /* SAFE */
00509       AST_RWLIST_HEAD_INIT(&(tableptr->columns));
00510 
00511       while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00512          SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00513 
00514          if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
00515             ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
00516             error = 1;
00517             break;
00518          }
00519          entry->name = (char *)entry + sizeof(*entry);
00520          strcpy(entry->name, columnname);
00521 
00522          SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00523          SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00524          SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00525          SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00526          SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00527          SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00528 
00529          /* Specification states that the octenlen should be the maximum number of bytes
00530           * returned in a char or binary column, but it seems that some drivers just set
00531           * it to NULL. (Bad Postgres! No biscuit!) */
00532          if (entry->octetlen == 0) {
00533             entry->octetlen = entry->size;
00534          }
00535 
00536          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);
00537          /* Insert column info into column list */
00538          AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00539       }
00540       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00541 
00542       AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00543       AST_RWLIST_RDLOCK(&(tableptr->columns));
00544       break;
00545    } while (1);
00546 
00547    AST_RWLIST_UNLOCK(&odbc_tables);
00548 
00549    if (error) {
00550       destroy_table_cache(tableptr);
00551       tableptr = NULL;
00552    }
00553    if (obj) {
00554       ast_odbc_release_obj(obj);
00555    }
00556    return tableptr;
00557 }
00558 
00559 struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
00560 {
00561    struct odbc_cache_columns *col;
00562    AST_RWLIST_TRAVERSE(&table->columns, col, list) {
00563       if (strcasecmp(col->name, colname) == 0) {
00564          return col;
00565       }
00566    }
00567    return NULL;
00568 }
00569 
00570 int ast_odbc_clear_cache(const char *database, const char *tablename)
00571 {
00572    struct odbc_cache_tables *tableptr;
00573 
00574    AST_RWLIST_WRLOCK(&odbc_tables);
00575    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
00576       if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00577          AST_LIST_REMOVE_CURRENT(list);
00578          destroy_table_cache(tableptr);
00579          break;
00580       }
00581    }
00582    AST_RWLIST_TRAVERSE_SAFE_END
00583    AST_RWLIST_UNLOCK(&odbc_tables);
00584    return tableptr ? 0 : -1;
00585 }
00586 
00587 SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
00588 {
00589    int attempt;
00590    SQLHSTMT stmt;
00591 
00592    for (attempt = 0; attempt < 2; attempt++) {
00593       stmt = exec_cb(obj, data);
00594 
00595       if (stmt) {
00596          break;
00597       } else if (obj->tx) {
00598          ast_log(LOG_WARNING, "Failed to execute, but unable to reconnect, as we're transactional.\n");
00599          break;
00600       } else if (attempt == 0) {
00601          ast_log(LOG_WARNING, "SQL Execute error! Verifying connection to %s [%s]...\n", obj->parent->name, obj->parent->dsn);
00602       }
00603       if (!ast_odbc_sanity_check(obj)) {
00604          break;
00605       }
00606    }
00607 
00608    return stmt;
00609 }
00610 
00611 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
00612 {
00613    int res = 0, i, attempt;
00614    SQLINTEGER nativeerror=0, numfields=0;
00615    SQLSMALLINT diagbytes=0;
00616    unsigned char state[10], diagnostic[256];
00617    SQLHSTMT stmt;
00618 
00619    for (attempt = 0; attempt < 2; attempt++) {
00620       /* This prepare callback may do more than just prepare -- it may also
00621        * bind parameters, bind results, etc.  The real key, here, is that
00622        * when we disconnect, all handles become invalid for most databases.
00623        * We must therefore redo everything when we establish a new
00624        * connection. */
00625       stmt = prepare_cb(obj, data);
00626 
00627       if (stmt) {
00628          res = SQLExecute(stmt);
00629          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00630             if (res == SQL_ERROR) {
00631                SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00632                for (i = 0; i < numfields; i++) {
00633                   SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00634                   ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00635                   if (i > 10) {
00636                      ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00637                      break;
00638                   }
00639                }
00640             }
00641 
00642             if (obj->tx) {
00643                ast_log(LOG_WARNING, "SQL Execute error, but unable to reconnect, as we're transactional.\n");
00644                break;
00645             } else {
00646                ast_log(LOG_WARNING, "SQL Execute error %d! Verifying connection to %s [%s]...\n", res, obj->parent->name, obj->parent->dsn);
00647                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00648                stmt = NULL;
00649 
00650                obj->up = 0;
00651                /*
00652                 * While this isn't the best way to try to correct an error, this won't automatically
00653                 * fail when the statement handle invalidates.
00654                 */
00655                if (!ast_odbc_sanity_check(obj)) {
00656                   break;
00657                }
00658                continue;
00659             }
00660          } else {
00661             obj->last_used = ast_tvnow();
00662          }
00663          break;
00664       } else if (attempt == 0) {
00665          ast_odbc_sanity_check(obj);
00666       }
00667    }
00668 
00669    return stmt;
00670 }
00671 
00672 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
00673 {
00674    int res = 0, i;
00675    SQLINTEGER nativeerror=0, numfields=0;
00676    SQLSMALLINT diagbytes=0;
00677    unsigned char state[10], diagnostic[256];
00678 
00679    res = SQLExecute(stmt);
00680    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00681       if (res == SQL_ERROR) {
00682          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00683          for (i = 0; i < numfields; i++) {
00684             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00685             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00686             if (i > 10) {
00687                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00688                break;
00689             }
00690          }
00691       }
00692    } else {
00693       obj->last_used = ast_tvnow();
00694    }
00695 
00696    return res;
00697 }
00698 
00699 SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
00700 {
00701    SQLRETURN res;
00702 
00703    if (pmaxlen == 0) {
00704       if (SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), 0, StrLen_or_Ind) == SQL_SUCCESS_WITH_INFO) {
00705          ast_str_make_space(buf, *StrLen_or_Ind + 1);
00706       }
00707    } else if (pmaxlen > 0) {
00708       ast_str_make_space(buf, pmaxlen);
00709    }
00710    res = SQLGetData(StatementHandle, ColumnNumber, TargetType, ast_str_buffer(*buf), ast_str_size(*buf), StrLen_or_Ind);
00711    ast_str_update(*buf);
00712 
00713    return res;
00714 }
00715 
00716 int ast_odbc_sanity_check(struct odbc_obj *obj) 
00717 {
00718    char *test_sql = "select 1";
00719    SQLHSTMT stmt;
00720    int res = 0;
00721 
00722    if (!ast_strlen_zero(obj->parent->sanitysql))
00723       test_sql = obj->parent->sanitysql;
00724 
00725    if (obj->up) {
00726       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00727       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00728          obj->up = 0;
00729       } else {
00730          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00731          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00732             obj->up = 0;
00733          } else {
00734             res = SQLExecute(stmt);
00735             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00736                obj->up = 0;
00737             }
00738          }
00739       }
00740       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00741    }
00742 
00743    if (!obj->up && !obj->tx) { /* Try to reconnect! */
00744       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00745       odbc_obj_disconnect(obj);
00746       odbc_obj_connect(obj);
00747    }
00748    return obj->up;
00749 }
00750 
00751 static int load_odbc_config(void)
00752 {
00753    static char *cfg = "res_odbc.conf";
00754    struct ast_config *config;
00755    struct ast_variable *v;
00756    char *cat;
00757    const char *dsn, *username, *password, *sanitysql;
00758    int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation;
00759    struct timeval ncache = { 0, 0 };
00760    unsigned int idlecheck;
00761    int preconnect = 0, res = 0;
00762    struct ast_flags config_flags = { 0 };
00763 
00764    struct odbc_class *new;
00765 
00766    config = ast_config_load(cfg, config_flags);
00767    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
00768       ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00769       return -1;
00770    }
00771    for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00772       if (!strcasecmp(cat, "ENV")) {
00773          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00774             setenv(v->name, v->value, 1);
00775             ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00776          }
00777       } else {
00778          /* Reset all to defaults for each class of odbc connections */
00779          dsn = username = password = sanitysql = NULL;
00780          enabled = 1;
00781          preconnect = idlecheck = 0;
00782          pooling = 0;
00783          limit = 0;
00784          bse = 1;
00785          conntimeout = 10;
00786          forcecommit = 0;
00787          isolation = SQL_TXN_READ_COMMITTED;
00788          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00789             if (!strcasecmp(v->name, "pooling")) {
00790                if (ast_true(v->value))
00791                   pooling = 1;
00792             } else if (!strncasecmp(v->name, "share", 5)) {
00793                /* "shareconnections" is a little clearer in meaning than "pooling" */
00794                if (ast_false(v->value))
00795                   pooling = 1;
00796             } else if (!strcasecmp(v->name, "limit")) {
00797                sscanf(v->value, "%30d", &limit);
00798                if (ast_true(v->value) && !limit) {
00799                   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);
00800                   limit = 1023;
00801                } else if (ast_false(v->value)) {
00802                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00803                   enabled = 0;
00804                   break;
00805                }
00806             } else if (!strcasecmp(v->name, "idlecheck")) {
00807                sscanf(v->value, "%30u", &idlecheck);
00808             } else if (!strcasecmp(v->name, "enabled")) {
00809                enabled = ast_true(v->value);
00810             } else if (!strcasecmp(v->name, "pre-connect")) {
00811                preconnect = ast_true(v->value);
00812             } else if (!strcasecmp(v->name, "dsn")) {
00813                dsn = v->value;
00814             } else if (!strcasecmp(v->name, "username")) {
00815                username = v->value;
00816             } else if (!strcasecmp(v->name, "password")) {
00817                password = v->value;
00818             } else if (!strcasecmp(v->name, "sanitysql")) {
00819                sanitysql = v->value;
00820             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00821                bse = ast_true(v->value);
00822             } else if (!strcasecmp(v->name, "connect_timeout")) {
00823                if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
00824                   ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
00825                   conntimeout = 10;
00826                }
00827             } else if (!strcasecmp(v->name, "negative_connection_cache")) {
00828                double dncache;
00829                if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
00830                   ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
00831                   /* 5 minutes sounds like a reasonable default */
00832                   ncache.tv_sec = 300;
00833                   ncache.tv_usec = 0;
00834                } else {
00835                   ncache.tv_sec = (int)dncache;
00836                   ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
00837                }
00838             } else if (!strcasecmp(v->name, "forcecommit")) {
00839                forcecommit = ast_true(v->value);
00840             } else if (!strcasecmp(v->name, "isolation")) {
00841                if ((isolation = text2isolation(v->value)) == 0) {
00842                   ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00843                   isolation = SQL_TXN_READ_COMMITTED;
00844                }
00845             }
00846          }
00847 
00848          if (enabled && !ast_strlen_zero(dsn)) {
00849             new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00850 
00851             if (!new) {
00852                res = -1;
00853                break;
00854             }
00855 
00856             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00857             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00858 
00859             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00860                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00861                ao2_ref(new, -1);
00862                return res;
00863             }
00864 
00865             new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00866 
00867             if (pooling) {
00868                new->haspool = pooling;
00869                if (limit) {
00870                   new->limit = limit;
00871                } else {
00872                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00873                   new->limit = 5;
00874                }
00875             }
00876 
00877             new->backslash_is_escape = bse ? 1 : 0;
00878             new->forcecommit = forcecommit ? 1 : 0;
00879             new->isolation = isolation;
00880             new->idlecheck = idlecheck;
00881             new->conntimeout = conntimeout;
00882             new->negative_connection_cache = ncache;
00883 
00884             if (cat)
00885                ast_copy_string(new->name, cat, sizeof(new->name));
00886             if (dsn)
00887                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00888             if (username && !(new->username = ast_strdup(username))) {
00889                ao2_ref(new, -1);
00890                break;
00891             }
00892             if (password && !(new->password = ast_strdup(password))) {
00893                ao2_ref(new, -1);
00894                break;
00895             }
00896             if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00897                ao2_ref(new, -1);
00898                break;
00899             }
00900 
00901             odbc_register_class(new, preconnect);
00902             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00903             ao2_ref(new, -1);
00904             new = NULL;
00905          }
00906       }
00907    }
00908    ast_config_destroy(config);
00909    return res;
00910 }
00911 
00912 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00913 {
00914    struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00915    struct odbc_class *class;
00916    struct odbc_obj *current;
00917    int length = 0;
00918    int which = 0;
00919    char *ret = NULL;
00920 
00921    switch (cmd) {
00922    case CLI_INIT:
00923       e->command = "odbc show";
00924       e->usage =
00925             "Usage: odbc show [class]\n"
00926             "       List settings of a particular ODBC class or,\n"
00927             "       if not specified, all classes.\n";
00928       return NULL;
00929    case CLI_GENERATE:
00930       if (a->pos != 2)
00931          return NULL;
00932       length = strlen(a->word);
00933       while ((class = ao2_iterator_next(&aoi))) {
00934          if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00935             ret = ast_strdup(class->name);
00936          }
00937          ao2_ref(class, -1);
00938          if (ret) {
00939             break;
00940          }
00941       }
00942       ao2_iterator_destroy(&aoi);
00943       if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00944          ret = ast_strdup("all");
00945       }
00946       return ret;
00947    }
00948 
00949    ast_cli(a->fd, "\nODBC DSN Settings\n");
00950    ast_cli(a->fd,   "-----------------\n\n");
00951    aoi = ao2_iterator_init(class_container, 0);
00952    while ((class = ao2_iterator_next(&aoi))) {
00953       if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00954          int count = 0;
00955          char timestr[80];
00956          struct ast_tm tm;
00957 
00958          ast_localtime(&class->last_negative_connect, &tm, NULL);
00959          ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
00960          ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn);
00961          ast_cli(a->fd, "    Last connection attempt: %s\n", timestr);
00962 
00963          if (class->haspool) {
00964             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00965 
00966             ast_cli(a->fd, "  Pooled: Yes\n  Limit:  %d\n  Connections in use: %d\n", class->limit, class->count);
00967 
00968             while ((current = ao2_iterator_next(&aoi2))) {
00969                ast_mutex_lock(&current->lock);
00970 #ifdef DEBUG_THREADS
00971                ast_cli(a->fd, "    - Connection %d: %s (%s:%d %s)\n", ++count,
00972                   current->used ? "in use" :
00973                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00974                   current->file, current->lineno, current->function);
00975 #else
00976                ast_cli(a->fd, "    - Connection %d: %s\n", ++count,
00977                   current->used ? "in use" :
00978                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00979 #endif
00980                ast_mutex_unlock(&current->lock);
00981                ao2_ref(current, -1);
00982             }
00983             ao2_iterator_destroy(&aoi2);
00984          } else {
00985             /* Should only ever be one of these (unless there are transactions) */
00986             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00987             while ((current = ao2_iterator_next(&aoi2))) {
00988                ast_cli(a->fd, "  Pooled: No\n  Connected: %s\n", current->used ? "In use" :
00989                   current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
00990                ao2_ref(current, -1);
00991             }
00992             ao2_iterator_destroy(&aoi2);
00993          }
00994          ast_cli(a->fd, "\n");
00995       }
00996       ao2_ref(class, -1);
00997    }
00998    ao2_iterator_destroy(&aoi);
00999 
01000    return CLI_SUCCESS;
01001 }
01002 
01003 static struct ast_cli_entry cli_odbc[] = {
01004    AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
01005 };
01006 
01007 static int odbc_register_class(struct odbc_class *class, int preconnect)
01008 {
01009    struct odbc_obj *obj;
01010    if (class) {
01011       ao2_link(class_container, class);
01012       /* I still have a reference in the caller, so a deref is NOT missing here. */
01013 
01014       if (preconnect) {
01015          /* Request and release builds a connection */
01016          obj = ast_odbc_request_obj(class->name, 0);
01017          if (obj) {
01018             ast_odbc_release_obj(obj);
01019          }
01020       }
01021 
01022       return 0;
01023    } else {
01024       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
01025       return -1;
01026    }
01027 }
01028 
01029 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
01030 {
01031    SQLINTEGER nativeerror=0, numfields=0;
01032    SQLSMALLINT diagbytes=0, i;
01033    unsigned char state[10], diagnostic[256];
01034 
01035    ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
01036    if (tx) {
01037       ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
01038       if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
01039          /* Handle possible transaction commit failure */
01040          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01041          for (i = 0; i < numfields; i++) {
01042             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01043             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01044             if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
01045                /* These codes mean that a commit failed and a transaction
01046                 * is still active. We must rollback, or things will get
01047                 * very, very weird for anybody using the handle next. */
01048                SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01049             }
01050             if (i > 10) {
01051                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01052                break;
01053             }
01054          }
01055       }
01056 
01057       /* Transaction is done, reset autocommit */
01058       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01059          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01060          for (i = 0; i < numfields; i++) {
01061             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01062             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01063             if (i > 10) {
01064                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01065                break;
01066             }
01067          }
01068       }
01069    }
01070 
01071 #ifdef DEBUG_THREADS
01072    obj->file[0] = '\0';
01073    obj->function[0] = '\0';
01074    obj->lineno = 0;
01075 #endif
01076 
01077    /* For pooled connections, this frees the connection to be
01078     * reused.  For non-pooled connections, it does nothing. */
01079    obj->used = 0;
01080    if (obj->txf) {
01081       /* Prevent recursion -- transaction is already closed out. */
01082       obj->txf->obj = NULL;
01083       obj->txf = release_transaction(obj->txf);
01084    }
01085    ao2_ref(obj, -1);
01086 }
01087 
01088 void ast_odbc_release_obj(struct odbc_obj *obj)
01089 {
01090    struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01091    odbc_release_obj2(obj, tx);
01092 }
01093 
01094 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
01095 {
01096    return obj->parent->backslash_is_escape;
01097 }
01098 
01099 static int commit_exec(struct ast_channel *chan, const char *data)
01100 {
01101    struct odbc_txn_frame *tx;
01102    SQLINTEGER nativeerror=0, numfields=0;
01103    SQLSMALLINT diagbytes=0, i;
01104    unsigned char state[10], diagnostic[256];
01105 
01106    if (ast_strlen_zero(data)) {
01107       tx = find_transaction(chan, NULL, NULL, 1);
01108    } else {
01109       tx = find_transaction(chan, NULL, data, 0);
01110    }
01111 
01112    pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01113 
01114    if (tx) {
01115       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01116          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01117          ast_str_reset(errors);
01118 
01119          /* Handle possible transaction commit failure */
01120          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01121          for (i = 0; i < numfields; i++) {
01122             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01123             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01124             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01125             if (i > 10) {
01126                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01127                break;
01128             }
01129          }
01130          pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01131       }
01132    }
01133    return 0;
01134 }
01135 
01136 static int rollback_exec(struct ast_channel *chan, const char *data)
01137 {
01138    struct odbc_txn_frame *tx;
01139    SQLINTEGER nativeerror=0, numfields=0;
01140    SQLSMALLINT diagbytes=0, i;
01141    unsigned char state[10], diagnostic[256];
01142 
01143    if (ast_strlen_zero(data)) {
01144       tx = find_transaction(chan, NULL, NULL, 1);
01145    } else {
01146       tx = find_transaction(chan, NULL, data, 0);
01147    }
01148 
01149    pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01150 
01151    if (tx) {
01152       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01153          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01154          ast_str_reset(errors);
01155 
01156          /* Handle possible transaction commit failure */
01157          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01158          for (i = 0; i < numfields; i++) {
01159             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01160             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01161             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01162             if (i > 10) {
01163                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01164                break;
01165             }
01166          }
01167          pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01168       }
01169    }
01170    return 0;
01171 }
01172 
01173 static int aoro2_class_cb(void *obj, void *arg, int flags)
01174 {
01175    struct odbc_class *class = obj;
01176    char *name = arg;
01177    if (!strcmp(class->name, name) && !class->delme) {
01178       return CMP_MATCH | CMP_STOP;
01179    }
01180    return 0;
01181 }
01182 
01183 #define USE_TX (void *)(long)1
01184 #define NO_TX  (void *)(long)2
01185 #define EOR_TX (void *)(long)3
01186 
01187 static int aoro2_obj_cb(void *vobj, void *arg, int flags)
01188 {
01189    struct odbc_obj *obj = vobj;
01190    ast_mutex_lock(&obj->lock);
01191    if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01192       obj->used = 1;
01193       ast_mutex_unlock(&obj->lock);
01194       return CMP_MATCH | CMP_STOP;
01195    }
01196    ast_mutex_unlock(&obj->lock);
01197    return 0;
01198 }
01199 
01200 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
01201 {
01202    struct odbc_obj *obj = NULL;
01203    struct odbc_class *class;
01204    SQLINTEGER nativeerror=0, numfields=0;
01205    SQLSMALLINT diagbytes=0, i;
01206    unsigned char state[10], diagnostic[256];
01207 
01208    if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01209       ast_debug(1, "Class '%s' not found!\n", name);
01210       return NULL;
01211    }
01212 
01213    ast_assert(ao2_ref(class, 0) > 1);
01214 
01215    if (class->haspool) {
01216       /* Recycle connections before building another */
01217       obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01218 
01219       if (obj) {
01220          ast_assert(ao2_ref(obj, 0) > 1);
01221       }
01222       if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
01223             (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
01224          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01225          if (!obj) {
01226             class->count--;
01227             ao2_ref(class, -1);
01228             ast_debug(3, "Unable to allocate object\n");
01229             ast_atomic_fetchadd_int(&class->count, -1);
01230             return NULL;
01231          }
01232          ast_assert(ao2_ref(obj, 0) == 1);
01233          ast_mutex_init(&obj->lock);
01234          /* obj inherits the outstanding reference to class */
01235          obj->parent = class;
01236          class = NULL;
01237          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01238             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01239             ast_assert(ao2_ref(obj->parent, 0) > 0);
01240             /* Because it was never within the container, we have to manually decrement the count here */
01241             ast_atomic_fetchadd_int(&obj->parent->count, -1);
01242             ao2_ref(obj, -1);
01243             obj = NULL;
01244          } else {
01245             obj->used = 1;
01246             ao2_link(obj->parent->obj_container, obj);
01247          }
01248       } else {
01249          /* If construction fails due to the limit (or negative timecache), reverse our increment. */
01250          if (!obj) {
01251             ast_atomic_fetchadd_int(&class->count, -1);
01252          }
01253          /* Object is not constructed, so delete outstanding reference to class. */
01254          ao2_ref(class, -1);
01255          class = NULL;
01256       }
01257 
01258       if (obj && ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01259          /* Ensure this connection has autocommit turned off. */
01260          if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01261             SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01262             for (i = 0; i < numfields; i++) {
01263                SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01264                ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01265                if (i > 10) {
01266                   ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01267                   break;
01268                }
01269             }
01270          }
01271       }
01272    } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01273       /* Non-pooled connections -- but must use a separate connection handle */
01274       if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01275          ast_debug(1, "Object not found\n");
01276          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01277          if (!obj) {
01278             ao2_ref(class, -1);
01279             ast_debug(3, "Unable to allocate object\n");
01280             return NULL;
01281          }
01282          ast_mutex_init(&obj->lock);
01283          /* obj inherits the outstanding reference to class */
01284          obj->parent = class;
01285          class = NULL;
01286          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01287             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01288             ao2_ref(obj, -1);
01289             obj = NULL;
01290          } else {
01291             obj->used = 1;
01292             ao2_link(obj->parent->obj_container, obj);
01293             ast_atomic_fetchadd_int(&obj->parent->count, +1);
01294          }
01295       }
01296 
01297       if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01298          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01299          for (i = 0; i < numfields; i++) {
01300             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01301             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01302             if (i > 10) {
01303                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01304                break;
01305             }
01306          }
01307       }
01308    } else {
01309       /* Non-pooled connection: multiple modules can use the same connection. */
01310       if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, NO_TX))) {
01311          /* Object is not constructed, so delete outstanding reference to class. */
01312          ast_assert(ao2_ref(class, 0) > 1);
01313          ao2_ref(class, -1);
01314          class = NULL;
01315       } else {
01316          /* No entry: build one */
01317          if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01318             ast_assert(ao2_ref(class, 0) > 1);
01319             ao2_ref(class, -1);
01320             ast_debug(3, "Unable to allocate object\n");
01321             return NULL;
01322          }
01323          ast_mutex_init(&obj->lock);
01324          /* obj inherits the outstanding reference to class */
01325          obj->parent = class;
01326          class = NULL;
01327          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01328             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01329             ao2_ref(obj, -1);
01330             obj = NULL;
01331          } else {
01332             ao2_link(obj->parent->obj_container, obj);
01333             ast_assert(ao2_ref(obj, 0) > 1);
01334          }
01335       }
01336 
01337       if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01338          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01339          for (i = 0; i < numfields; i++) {
01340             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01341             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01342             if (i > 10) {
01343                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01344                break;
01345             }
01346          }
01347       }
01348    }
01349 
01350    /* Set the isolation property */
01351    if (obj && SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01352       SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01353       for (i = 0; i < numfields; i++) {
01354          SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01355          ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01356          if (i > 10) {
01357             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01358             break;
01359          }
01360       }
01361    }
01362 
01363    if (obj && ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
01364       /* Check if this connection qualifies for reconnection, with negative connection cache time */
01365       if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
01366          odbc_obj_connect(obj);
01367       }
01368    } else if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01369       ast_odbc_sanity_check(obj);
01370    } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
01371       odbc_obj_connect(obj);
01372    }
01373 
01374 #ifdef DEBUG_THREADS
01375    if (obj) {
01376       ast_copy_string(obj->file, file, sizeof(obj->file));
01377       ast_copy_string(obj->function, function, sizeof(obj->function));
01378       obj->lineno = lineno;
01379    }
01380 #endif
01381    ast_assert(class == NULL);
01382 
01383    if (obj) {
01384       ast_assert(ao2_ref(obj, 0) > 1);
01385    }
01386    return obj;
01387 }
01388 
01389 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
01390 {
01391    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01392    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01393 }
01394 
01395 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
01396 {
01397    struct ast_datastore *txn_store;
01398    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01399    struct odbc_txn_frame *txn = NULL;
01400 
01401    if (!chan) {
01402       /* No channel == no transaction */
01403       return NULL;
01404    }
01405 
01406    ast_channel_lock(chan);
01407    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01408       oldlist = txn_store->data;
01409    } else {
01410       ast_channel_unlock(chan);
01411       return NULL;
01412    }
01413 
01414    AST_LIST_LOCK(oldlist);
01415    ast_channel_unlock(chan);
01416 
01417    AST_LIST_TRAVERSE(oldlist, txn, list) {
01418       if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01419          AST_LIST_UNLOCK(oldlist);
01420          return txn->obj;
01421       }
01422    }
01423    AST_LIST_UNLOCK(oldlist);
01424    return NULL;
01425 }
01426 
01427 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
01428 {
01429    int res;
01430    SQLINTEGER err;
01431    short int mlen;
01432    unsigned char msg[200], state[10];
01433 
01434    /* Nothing to disconnect */
01435    if (!obj->con) {
01436       return ODBC_SUCCESS;
01437    }
01438 
01439    ast_mutex_lock(&obj->lock);
01440 
01441    res = SQLDisconnect(obj->con);
01442 
01443    if (obj->parent) {
01444       if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01445          ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01446       } else {
01447          ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01448       }
01449    }
01450 
01451    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
01452       obj->con = NULL;
01453       ast_log(LOG_DEBUG, "Database handle deallocated\n");
01454    } else {
01455       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01456       ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
01457    }
01458 
01459    obj->up = 0;
01460    ast_mutex_unlock(&obj->lock);
01461    return ODBC_SUCCESS;
01462 }
01463 
01464 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
01465 {
01466    int res;
01467    SQLINTEGER err;
01468    short int mlen;
01469    unsigned char msg[200], state[10];
01470 #ifdef NEEDTRACE
01471    SQLINTEGER enable = 1;
01472    char *tracefile = "/tmp/odbc.trace";
01473 #endif
01474    ast_mutex_lock(&obj->lock);
01475 
01476    if (obj->up) {
01477       odbc_obj_disconnect(obj);
01478       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01479    } else {
01480       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01481    }
01482 
01483    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
01484 
01485    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01486       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01487       obj->parent->last_negative_connect = ast_tvnow();
01488       ast_mutex_unlock(&obj->lock);
01489       return ODBC_FAIL;
01490    }
01491    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01492    SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01493 #ifdef NEEDTRACE
01494    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01495    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01496 #endif
01497 
01498    res = SQLConnect(obj->con,
01499          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01500          (SQLCHAR *) obj->parent->username, SQL_NTS,
01501          (SQLCHAR *) obj->parent->password, SQL_NTS);
01502 
01503    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01504       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
01505       obj->parent->last_negative_connect = ast_tvnow();
01506       ast_mutex_unlock(&obj->lock);
01507       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01508       return ODBC_FAIL;
01509    } else {
01510       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01511       obj->up = 1;
01512       obj->last_used = ast_tvnow();
01513    }
01514 
01515    ast_mutex_unlock(&obj->lock);
01516    return ODBC_SUCCESS;
01517 }
01518 
01519 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01520 {
01521    AST_DECLARE_APP_ARGS(args,
01522       AST_APP_ARG(property);
01523       AST_APP_ARG(opt);
01524    );
01525    struct odbc_txn_frame *tx;
01526 
01527    AST_STANDARD_APP_ARGS(args, data);
01528    if (strcasecmp(args.property, "transaction") == 0) {
01529       if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01530          ast_copy_string(buf, tx->name, len);
01531          return 0;
01532       }
01533    } else if (strcasecmp(args.property, "isolation") == 0) {
01534       if (!ast_strlen_zero(args.opt)) {
01535          tx = find_transaction(chan, NULL, args.opt, 0);
01536       } else {
01537          tx = find_transaction(chan, NULL, NULL, 1);
01538       }
01539       if (tx) {
01540          ast_copy_string(buf, isolation2text(tx->isolation), len);
01541          return 0;
01542       }
01543    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01544       if (!ast_strlen_zero(args.opt)) {
01545          tx = find_transaction(chan, NULL, args.opt, 0);
01546       } else {
01547          tx = find_transaction(chan, NULL, NULL, 1);
01548       }
01549       if (tx) {
01550          ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01551          return 0;
01552       }
01553    }
01554    return -1;
01555 }
01556 
01557 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
01558 {
01559    AST_DECLARE_APP_ARGS(args,
01560       AST_APP_ARG(property);
01561       AST_APP_ARG(opt);
01562    );
01563    struct odbc_txn_frame *tx;
01564    SQLINTEGER nativeerror=0, numfields=0;
01565    SQLSMALLINT diagbytes=0, i;
01566    unsigned char state[10], diagnostic[256];
01567 
01568    AST_STANDARD_APP_ARGS(args, s);
01569    if (strcasecmp(args.property, "transaction") == 0) {
01570       /* Set active transaction */
01571       struct odbc_obj *obj;
01572       if ((tx = find_transaction(chan, NULL, value, 0))) {
01573          mark_transaction_active(chan, tx);
01574       } else {
01575          /* No such transaction, create one */
01576          struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01577          if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01578             ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01579             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01580             return -1;
01581          }
01582          if (!(tx = find_transaction(chan, obj, value, 0))) {
01583             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01584             return -1;
01585          }
01586          obj->tx = 1;
01587       }
01588       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01589       return 0;
01590    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01591       /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
01592       if (ast_strlen_zero(args.opt)) {
01593          tx = find_transaction(chan, NULL, NULL, 1);
01594       } else {
01595          tx = find_transaction(chan, NULL, args.opt, 0);
01596       }
01597       if (!tx) {
01598          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01599          return -1;
01600       }
01601       if (ast_true(value)) {
01602          tx->forcecommit = 1;
01603       } else if (ast_false(value)) {
01604          tx->forcecommit = 0;
01605       } else {
01606          ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01607          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01608          return -1;
01609       }
01610 
01611       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01612       return 0;
01613    } else if (strcasecmp(args.property, "isolation") == 0) {
01614       /* How do uncommitted transactions affect reads? */
01615       int isolation = text2isolation(value);
01616       if (ast_strlen_zero(args.opt)) {
01617          tx = find_transaction(chan, NULL, NULL, 1);
01618       } else {
01619          tx = find_transaction(chan, NULL, args.opt, 0);
01620       }
01621       if (!tx) {
01622          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01623          return -1;
01624       }
01625       if (isolation == 0) {
01626          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01627          ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01628       } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01629          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01630          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01631          for (i = 0; i < numfields; i++) {
01632             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01633             ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01634             if (i > 10) {
01635                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01636                break;
01637             }
01638          }
01639       } else {
01640          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01641          tx->isolation = isolation;
01642       }
01643       return 0;
01644    } else {
01645       ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01646       return -1;
01647    }
01648 }
01649 
01650 static struct ast_custom_function odbc_function = {
01651    .name = "ODBC",
01652    .read = acf_transaction_read,
01653    .write = acf_transaction_write,
01654 };
01655 
01656 static const char * const app_commit = "ODBC_Commit";
01657 static const char * const app_rollback = "ODBC_Rollback";
01658 
01659 /*!
01660  * \internal
01661  * \brief Implements the channels provider.
01662  */
01663 static int data_odbc_provider_handler(const struct ast_data_search *search,
01664       struct ast_data *root)
01665 {
01666    struct ao2_iterator aoi, aoi2;
01667    struct odbc_class *class;
01668    struct odbc_obj *current;
01669    struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection;
01670    struct ast_data *enum_node;
01671    int count;
01672 
01673    aoi = ao2_iterator_init(class_container, 0);
01674    while ((class = ao2_iterator_next(&aoi))) {
01675       data_odbc_class = ast_data_add_node(root, "class");
01676       if (!data_odbc_class) {
01677          ao2_ref(class, -1);
01678          continue;
01679       }
01680 
01681       ast_data_add_structure(odbc_class, data_odbc_class, class);
01682 
01683       if (!ao2_container_count(class->obj_container)) {
01684          ao2_ref(class, -1);
01685          continue;
01686       }
01687 
01688       data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
01689       if (!data_odbc_connections) {
01690          ao2_ref(class, -1);
01691          continue;
01692       }
01693 
01694       ast_data_add_bool(data_odbc_class, "shared", !class->haspool);
01695       /* isolation */
01696       enum_node = ast_data_add_node(data_odbc_class, "isolation");
01697       if (!enum_node) {
01698          ao2_ref(class, -1);
01699          continue;
01700       }
01701       ast_data_add_int(enum_node, "value", class->isolation);
01702       ast_data_add_str(enum_node, "text", isolation2text(class->isolation));
01703 
01704       count = 0;
01705       aoi2 = ao2_iterator_init(class->obj_container, 0);
01706       while ((current = ao2_iterator_next(&aoi2))) {
01707          data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection");
01708          if (!data_odbc_connection) {
01709             ao2_ref(current, -1);
01710             continue;
01711          }
01712 
01713          ast_mutex_lock(&current->lock);
01714          ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" :
01715                current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
01716          ast_data_add_bool(data_odbc_connection, "transactional", current->tx);
01717          ast_mutex_unlock(&current->lock);
01718 
01719          if (class->haspool) {
01720             ast_data_add_int(data_odbc_connection, "number", ++count);
01721          }
01722 
01723          ao2_ref(current, -1);
01724       }
01725       ao2_ref(class, -1);
01726 
01727       if (!ast_data_search_match(search, data_odbc_class)) {
01728          ast_data_remove_node(root, data_odbc_class);
01729       }
01730    }
01731    return 0;
01732 }
01733 
01734 /*!
01735  * \internal
01736  * \brief /asterisk/res/odbc/listprovider.
01737  */
01738 static const struct ast_data_handler odbc_provider = {
01739    .version = AST_DATA_HANDLER_VERSION,
01740    .get = data_odbc_provider_handler
01741 };
01742 
01743 static const struct ast_data_entry odbc_providers[] = {
01744    AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
01745 };
01746 
01747 static int reload(void)
01748 {
01749    struct odbc_cache_tables *table;
01750    struct odbc_class *class;
01751    struct odbc_obj *current;
01752    struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01753 
01754    /* First, mark all to be purged */
01755    while ((class = ao2_iterator_next(&aoi))) {
01756       class->delme = 1;
01757       ao2_ref(class, -1);
01758    }
01759    ao2_iterator_destroy(&aoi);
01760 
01761    load_odbc_config();
01762 
01763    /* Purge remaining classes */
01764 
01765    /* Note on how this works; this is a case of circular references, so we
01766     * explicitly do NOT want to use a callback here (or we wind up in
01767     * recursive hell).
01768     *
01769     * 1. Iterate through all the classes.  Note that the classes will currently
01770     * contain two classes of the same name, one of which is marked delme and
01771     * will be purged when all remaining objects of the class are released, and
01772     * the other, which was created above when we re-parsed the config file.
01773     * 2. On each class, there is a reference held by the master container and
01774     * a reference held by each connection object.  There are two cases for
01775     * destruction of the class, noted below.  However, in all cases, all O-refs
01776     * (references to objects) will first be freed, which will cause the C-refs
01777     * (references to classes) to be decremented (but never to 0, because the
01778     * class container still has a reference).
01779     *    a) If the class has outstanding objects, the C-ref by the class
01780     *    container will then be freed, which leaves only C-refs by any
01781     *    outstanding objects.  When the final outstanding object is released
01782     *    (O-refs held by applications and dialplan functions), it will in turn
01783     *    free the final C-ref, causing class destruction.
01784     *    b) If the class has no outstanding objects, when the class container
01785     *    removes the final C-ref, the class will be destroyed.
01786     */
01787    aoi = ao2_iterator_init(class_container, 0);
01788    while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
01789       if (class->delme) {
01790          struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01791          while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
01792             ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
01793             ao2_ref(current, -1); /* O-ref-- (by iterator) */
01794             /* At this point, either
01795              * a) there's an outstanding O-ref, or
01796              * b) the object has already been destroyed.
01797              */
01798          }
01799          ao2_iterator_destroy(&aoi2);
01800          ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
01801          /* At this point, either
01802           * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
01803           * b) the last remaining C-ref is held by the iterator, which will be
01804           * destroyed in the next step.
01805           */
01806       }
01807       ao2_ref(class, -1); /* C-ref-- (by iterator) */
01808    }
01809    ao2_iterator_destroy(&aoi);
01810 
01811    /* Empty the cache; it will get rebuilt the next time the tables are needed. */
01812    AST_RWLIST_WRLOCK(&odbc_tables);
01813    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01814       destroy_table_cache(table);
01815    }
01816    AST_RWLIST_UNLOCK(&odbc_tables);
01817 
01818    return 0;
01819 }
01820 
01821 static int unload_module(void)
01822 {
01823    /* Prohibit unloading */
01824    return -1;
01825 }
01826 
01827 static int load_module(void)
01828 {
01829    if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
01830       return AST_MODULE_LOAD_DECLINE;
01831    if (load_odbc_config() == -1)
01832       return AST_MODULE_LOAD_DECLINE;
01833    ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
01834    ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers));
01835    ast_register_application_xml(app_commit, commit_exec);
01836    ast_register_application_xml(app_rollback, rollback_exec);
01837    ast_custom_function_register(&odbc_function);
01838    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
01839    return 0;
01840 }
01841 
01842 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "ODBC resource",
01843       .load = load_module,
01844       .unload = unload_module,
01845       .reload = reload,
01846       .load_pri = AST_MODPRI_REALTIME_DEPEND,
01847           );