Mon Jun 18 2012 03:55:39

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