Sat Apr 26 2014 22:01:40

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