Mon Oct 8 12:39:04 2012

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: 370183 $")
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 const 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    if (!chan) {
00369       return -1;
00370    }
00371 
00372    ast_channel_lock(chan);
00373    if (!(txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
00374       ast_channel_unlock(chan);
00375       return -1;
00376    }
00377 
00378    oldlist = txn_store->data;
00379    AST_LIST_LOCK(oldlist);
00380    AST_LIST_TRAVERSE(oldlist, txn, list) {
00381       if (txn == tx) {
00382          txn->active = 1;
00383          active = txn;
00384       } else {
00385          txn->active = 0;
00386       }
00387    }
00388    AST_LIST_UNLOCK(oldlist);
00389    ast_channel_unlock(chan);
00390    return active ? 0 : -1;
00391 }
00392 
00393 static void odbc_class_destructor(void *data)
00394 {
00395    struct odbc_class *class = data;
00396    /* Due to refcounts, we can safely assume that any objects with a reference
00397     * to us will prevent our destruction, so we don't need to worry about them.
00398     */
00399    if (class->username) {
00400       ast_free(class->username);
00401    }
00402    if (class->password) {
00403       ast_free(class->password);
00404    }
00405    if (class->sanitysql) {
00406       ast_free(class->sanitysql);
00407    }
00408    ao2_ref(class->obj_container, -1);
00409    SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00410 }
00411 
00412 static int null_hash_fn(const void *obj, const int flags)
00413 {
00414    return 0;
00415 }
00416 
00417 static void odbc_obj_destructor(void *data)
00418 {
00419    struct odbc_obj *obj = data;
00420    struct odbc_class *class = obj->parent;
00421    obj->parent = NULL;
00422    odbc_obj_disconnect(obj);
00423    ast_mutex_destroy(&obj->lock);
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    ast_mutex_lock(&obj->lock);
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    ast_mutex_unlock(&obj->lock);
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    ast_mutex_lock(&obj->lock);
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    ast_mutex_unlock(&obj->lock);
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    ast_mutex_lock(&obj->lock);
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    ast_mutex_unlock(&obj->lock);
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    ast_mutex_lock(&obj->lock);
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    ast_mutex_unlock(&obj->lock);
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;
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          isolation = SQL_TXN_READ_COMMITTED;
00806          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00807             if (!strcasecmp(v->name, "pooling")) {
00808                if (ast_true(v->value))
00809                   pooling = 1;
00810             } else if (!strncasecmp(v->name, "share", 5)) {
00811                /* "shareconnections" is a little clearer in meaning than "pooling" */
00812                if (ast_false(v->value))
00813                   pooling = 1;
00814             } else if (!strcasecmp(v->name, "limit")) {
00815                sscanf(v->value, "%30d", &limit);
00816                if (ast_true(v->value) && !limit) {
00817                   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);
00818                   limit = 1023;
00819                } else if (ast_false(v->value)) {
00820                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00821                   enabled = 0;
00822                   break;
00823                }
00824             } else if (!strcasecmp(v->name, "idlecheck")) {
00825                sscanf(v->value, "%30u", &idlecheck);
00826             } else if (!strcasecmp(v->name, "enabled")) {
00827                enabled = ast_true(v->value);
00828             } else if (!strcasecmp(v->name, "pre-connect")) {
00829                preconnect = ast_true(v->value);
00830             } else if (!strcasecmp(v->name, "dsn")) {
00831                dsn = v->value;
00832             } else if (!strcasecmp(v->name, "username")) {
00833                username = v->value;
00834             } else if (!strcasecmp(v->name, "password")) {
00835                password = v->value;
00836             } else if (!strcasecmp(v->name, "sanitysql")) {
00837                sanitysql = v->value;
00838             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00839                bse = ast_true(v->value);
00840             } else if (!strcasecmp(v->name, "connect_timeout")) {
00841                if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
00842                   ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
00843                   conntimeout = 10;
00844                }
00845             } else if (!strcasecmp(v->name, "negative_connection_cache")) {
00846                double dncache;
00847                if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
00848                   ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
00849                   /* 5 minutes sounds like a reasonable default */
00850                   ncache.tv_sec = 300;
00851                   ncache.tv_usec = 0;
00852                } else {
00853                   ncache.tv_sec = (int)dncache;
00854                   ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
00855                }
00856             } else if (!strcasecmp(v->name, "forcecommit")) {
00857                forcecommit = ast_true(v->value);
00858             } else if (!strcasecmp(v->name, "isolation")) {
00859                if ((isolation = text2isolation(v->value)) == 0) {
00860                   ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
00861                   isolation = SQL_TXN_READ_COMMITTED;
00862                }
00863             }
00864          }
00865 
00866          if (enabled && !ast_strlen_zero(dsn)) {
00867             new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00868 
00869             if (!new) {
00870                res = -1;
00871                break;
00872             }
00873 
00874             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00875             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00876 
00877             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00878                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00879                ao2_ref(new, -1);
00880                return res;
00881             }
00882 
00883             new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00884 
00885             if (pooling) {
00886                new->haspool = pooling;
00887                if (limit) {
00888                   new->limit = limit;
00889                } else {
00890                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00891                   new->limit = 5;
00892                }
00893             }
00894 
00895             new->backslash_is_escape = bse ? 1 : 0;
00896             new->forcecommit = forcecommit ? 1 : 0;
00897             new->isolation = isolation;
00898             new->idlecheck = idlecheck;
00899             new->conntimeout = conntimeout;
00900             new->negative_connection_cache = ncache;
00901 
00902             if (cat)
00903                ast_copy_string(new->name, cat, sizeof(new->name));
00904             if (dsn)
00905                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00906             if (username && !(new->username = ast_strdup(username))) {
00907                ao2_ref(new, -1);
00908                break;
00909             }
00910             if (password && !(new->password = ast_strdup(password))) {
00911                ao2_ref(new, -1);
00912                break;
00913             }
00914             if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00915                ao2_ref(new, -1);
00916                break;
00917             }
00918 
00919             odbc_register_class(new, preconnect);
00920             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00921             ao2_ref(new, -1);
00922             new = NULL;
00923          }
00924       }
00925    }
00926    ast_config_destroy(config);
00927    return res;
00928 }
00929 
00930 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00931 {
00932    struct ao2_iterator aoi;
00933    struct odbc_class *class;
00934    struct odbc_obj *current;
00935    int length = 0;
00936    int which = 0;
00937    char *ret = NULL;
00938 
00939    switch (cmd) {
00940    case CLI_INIT:
00941       e->command = "odbc show";
00942       e->usage =
00943             "Usage: odbc show [class]\n"
00944             "       List settings of a particular ODBC class or,\n"
00945             "       if not specified, all classes.\n";
00946       return NULL;
00947    case CLI_GENERATE:
00948       if (a->pos != 2)
00949          return NULL;
00950       length = strlen(a->word);
00951       aoi = ao2_iterator_init(class_container, 0);
00952       while ((class = ao2_iterator_next(&aoi))) {
00953          if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00954             ret = ast_strdup(class->name);
00955          }
00956          ao2_ref(class, -1);
00957          if (ret) {
00958             break;
00959          }
00960       }
00961       ao2_iterator_destroy(&aoi);
00962       if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00963          ret = ast_strdup("all");
00964       }
00965       return ret;
00966    }
00967 
00968    ast_cli(a->fd, "\nODBC DSN Settings\n");
00969    ast_cli(a->fd,   "-----------------\n\n");
00970    aoi = ao2_iterator_init(class_container, 0);
00971    while ((class = ao2_iterator_next(&aoi))) {
00972       if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00973          int count = 0;
00974          char timestr[80];
00975          struct ast_tm tm;
00976 
00977          ast_localtime(&class->last_negative_connect, &tm, NULL);
00978          ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
00979          ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn);
00980          ast_cli(a->fd, "    Last connection attempt: %s\n", timestr);
00981 
00982          if (class->haspool) {
00983             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00984 
00985             ast_cli(a->fd, "  Pooled: Yes\n  Limit:  %d\n  Connections in use: %d\n", class->limit, class->count);
00986 
00987             while ((current = ao2_iterator_next(&aoi2))) {
00988                ast_mutex_lock(&current->lock);
00989 #ifdef DEBUG_THREADS
00990                ast_cli(a->fd, "    - Connection %d: %s (%s:%d %s)\n", ++count,
00991                   current->used ? "in use" :
00992                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00993                   current->file, current->lineno, current->function);
00994 #else
00995                ast_cli(a->fd, "    - Connection %d: %s\n", ++count,
00996                   current->used ? "in use" :
00997                   current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00998 #endif
00999                ast_mutex_unlock(&current->lock);
01000                ao2_ref(current, -1);
01001             }
01002             ao2_iterator_destroy(&aoi2);
01003          } else {
01004             /* Should only ever be one of these (unless there are transactions) */
01005             struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01006             while ((current = ao2_iterator_next(&aoi2))) {
01007                ast_cli(a->fd, "  Pooled: No\n  Connected: %s\n", current->used ? "In use" :
01008                   current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
01009                ao2_ref(current, -1);
01010             }
01011             ao2_iterator_destroy(&aoi2);
01012          }
01013          ast_cli(a->fd, "\n");
01014       }
01015       ao2_ref(class, -1);
01016    }
01017    ao2_iterator_destroy(&aoi);
01018 
01019    return CLI_SUCCESS;
01020 }
01021 
01022 static struct ast_cli_entry cli_odbc[] = {
01023    AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
01024 };
01025 
01026 static int odbc_register_class(struct odbc_class *class, int preconnect)
01027 {
01028    struct odbc_obj *obj;
01029    if (class) {
01030       ao2_link(class_container, class);
01031       /* I still have a reference in the caller, so a deref is NOT missing here. */
01032 
01033       if (preconnect) {
01034          /* Request and release builds a connection */
01035          obj = ast_odbc_request_obj(class->name, 0);
01036          if (obj) {
01037             ast_odbc_release_obj(obj);
01038          }
01039       }
01040 
01041       return 0;
01042    } else {
01043       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
01044       return -1;
01045    }
01046 }
01047 
01048 static void odbc_release_obj2(struct odbc_obj *obj, struct odbc_txn_frame *tx)
01049 {
01050    SQLINTEGER nativeerror=0, numfields=0;
01051    SQLSMALLINT diagbytes=0, i;
01052    unsigned char state[10], diagnostic[256];
01053 
01054    ast_debug(2, "odbc_release_obj2(%p) called (obj->txf = %p)\n", obj, obj->txf);
01055    if (tx) {
01056       ast_debug(1, "called on a transactional handle with %s\n", tx->forcecommit ? "COMMIT" : "ROLLBACK");
01057       if (SQLEndTran(SQL_HANDLE_DBC, obj->con, tx->forcecommit ? SQL_COMMIT : SQL_ROLLBACK) == SQL_ERROR) {
01058          /* Handle possible transaction commit failure */
01059          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01060          for (i = 0; i < numfields; i++) {
01061             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01062             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01063             if (!strcmp((char *)state, "25S02") || !strcmp((char *)state, "08007")) {
01064                /* These codes mean that a commit failed and a transaction
01065                 * is still active. We must rollback, or things will get
01066                 * very, very weird for anybody using the handle next. */
01067                SQLEndTran(SQL_HANDLE_DBC, obj->con, SQL_ROLLBACK);
01068             }
01069             if (i > 10) {
01070                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01071                break;
01072             }
01073          }
01074       }
01075 
01076       /* Transaction is done, reset autocommit */
01077       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01078          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01079          for (i = 0; i < numfields; i++) {
01080             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01081             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01082             if (i > 10) {
01083                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01084                break;
01085             }
01086          }
01087       }
01088    }
01089 
01090 #ifdef DEBUG_THREADS
01091    obj->file[0] = '\0';
01092    obj->function[0] = '\0';
01093    obj->lineno = 0;
01094 #endif
01095 
01096    /* For pooled connections, this frees the connection to be
01097     * reused.  For non-pooled connections, it does nothing. */
01098    obj->used = 0;
01099    if (obj->txf) {
01100       /* Prevent recursion -- transaction is already closed out. */
01101       obj->txf->obj = NULL;
01102       obj->txf = release_transaction(obj->txf);
01103    }
01104    ao2_ref(obj, -1);
01105 }
01106 
01107 void ast_odbc_release_obj(struct odbc_obj *obj)
01108 {
01109    struct odbc_txn_frame *tx = find_transaction(NULL, obj, NULL, 0);
01110    odbc_release_obj2(obj, tx);
01111 }
01112 
01113 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
01114 {
01115    return obj->parent->backslash_is_escape;
01116 }
01117 
01118 static int commit_exec(struct ast_channel *chan, const char *data)
01119 {
01120    struct odbc_txn_frame *tx;
01121    SQLINTEGER nativeerror=0, numfields=0;
01122    SQLSMALLINT diagbytes=0, i;
01123    unsigned char state[10], diagnostic[256];
01124 
01125    if (ast_strlen_zero(data)) {
01126       tx = find_transaction(chan, NULL, NULL, 1);
01127    } else {
01128       tx = find_transaction(chan, NULL, data, 0);
01129    }
01130 
01131    pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", "OK");
01132 
01133    if (tx) {
01134       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_COMMIT) == SQL_ERROR) {
01135          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01136          ast_str_reset(errors);
01137 
01138          /* Handle possible transaction commit failure */
01139          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01140          for (i = 0; i < numfields; i++) {
01141             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01142             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01143             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01144             if (i > 10) {
01145                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01146                break;
01147             }
01148          }
01149          pbx_builtin_setvar_helper(chan, "COMMIT_RESULT", ast_str_buffer(errors));
01150       }
01151    }
01152    return 0;
01153 }
01154 
01155 static int rollback_exec(struct ast_channel *chan, const char *data)
01156 {
01157    struct odbc_txn_frame *tx;
01158    SQLINTEGER nativeerror=0, numfields=0;
01159    SQLSMALLINT diagbytes=0, i;
01160    unsigned char state[10], diagnostic[256];
01161 
01162    if (ast_strlen_zero(data)) {
01163       tx = find_transaction(chan, NULL, NULL, 1);
01164    } else {
01165       tx = find_transaction(chan, NULL, data, 0);
01166    }
01167 
01168    pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", "OK");
01169 
01170    if (tx) {
01171       if (SQLEndTran(SQL_HANDLE_DBC, tx->obj->con, SQL_ROLLBACK) == SQL_ERROR) {
01172          struct ast_str *errors = ast_str_thread_get(&errors_buf, 16);
01173          ast_str_reset(errors);
01174 
01175          /* Handle possible transaction commit failure */
01176          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01177          for (i = 0; i < numfields; i++) {
01178             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01179             ast_str_append(&errors, 0, "%s%s", ast_str_strlen(errors) ? "," : "", state);
01180             ast_log(LOG_WARNING, "SQLEndTran returned an error: %s: %s\n", state, diagnostic);
01181             if (i > 10) {
01182                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01183                break;
01184             }
01185          }
01186          pbx_builtin_setvar_helper(chan, "ROLLBACK_RESULT", ast_str_buffer(errors));
01187       }
01188    }
01189    return 0;
01190 }
01191 
01192 static int aoro2_class_cb(void *obj, void *arg, int flags)
01193 {
01194    struct odbc_class *class = obj;
01195    char *name = arg;
01196    if (!strcmp(class->name, name) && !class->delme) {
01197       return CMP_MATCH | CMP_STOP;
01198    }
01199    return 0;
01200 }
01201 
01202 #define USE_TX (void *)(long)1
01203 #define NO_TX  (void *)(long)2
01204 #define EOR_TX (void *)(long)3
01205 
01206 static int aoro2_obj_cb(void *vobj, void *arg, int flags)
01207 {
01208    struct odbc_obj *obj = vobj;
01209    ast_mutex_lock(&obj->lock);
01210    if ((arg == NO_TX && !obj->tx) || (arg == EOR_TX && !obj->used) || (arg == USE_TX && obj->tx && !obj->used)) {
01211       obj->used = 1;
01212       ast_mutex_unlock(&obj->lock);
01213       return CMP_MATCH | CMP_STOP;
01214    }
01215    ast_mutex_unlock(&obj->lock);
01216    return 0;
01217 }
01218 
01219 /* This function should only be called for shared connections. Otherwise, the lack of
01220  * setting vobj->used breaks EOR_TX searching. For nonshared connections, use
01221  * aoro2_obj_cb instead. */
01222 static int aoro2_obj_notx_cb(void *vobj, void *arg, int flags)
01223 {
01224    struct odbc_obj *obj = vobj;
01225    if (!obj->tx) {
01226       return CMP_MATCH | CMP_STOP;
01227    }
01228    return 0;
01229 }
01230 
01231 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
01232 {
01233    struct odbc_obj *obj = NULL;
01234    struct odbc_class *class;
01235    SQLINTEGER nativeerror=0, numfields=0;
01236    SQLSMALLINT diagbytes=0, i;
01237    unsigned char state[10], diagnostic[256];
01238 
01239    if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
01240       ast_debug(1, "Class '%s' not found!\n", name);
01241       return NULL;
01242    }
01243 
01244    ast_assert(ao2_ref(class, 0) > 1);
01245 
01246    if (class->haspool) {
01247       /* Recycle connections before building another */
01248       obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, EOR_TX);
01249 
01250       if (obj) {
01251          ast_assert(ao2_ref(obj, 0) > 1);
01252       }
01253       if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
01254             (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
01255          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01256          if (!obj) {
01257             class->count--;
01258             ao2_ref(class, -1);
01259             ast_debug(3, "Unable to allocate object\n");
01260             ast_atomic_fetchadd_int(&class->count, -1);
01261             return NULL;
01262          }
01263          ast_assert(ao2_ref(obj, 0) == 1);
01264          ast_mutex_init(&obj->lock);
01265          /* obj inherits the outstanding reference to class */
01266          obj->parent = class;
01267          class = NULL;
01268          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01269             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01270             ast_assert(ao2_ref(obj->parent, 0) > 0);
01271             /* Because it was never within the container, we have to manually decrement the count here */
01272             ast_atomic_fetchadd_int(&obj->parent->count, -1);
01273             ao2_ref(obj, -1);
01274             obj = NULL;
01275          } else {
01276             obj->used = 1;
01277             ao2_link(obj->parent->obj_container, obj);
01278          }
01279       } else {
01280          /* If construction fails due to the limit (or negative timecache), reverse our increment. */
01281          if (!obj) {
01282             ast_atomic_fetchadd_int(&class->count, -1);
01283          }
01284          /* Object is not constructed, so delete outstanding reference to class. */
01285          ao2_ref(class, -1);
01286          class = NULL;
01287       }
01288 
01289       if (!obj) {
01290          return NULL;
01291       }
01292 
01293       ast_mutex_lock(&obj->lock);
01294 
01295       if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01296          /* Ensure this connection has autocommit turned off. */
01297          if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01298             SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01299             for (i = 0; i < numfields; i++) {
01300                SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01301                ast_log(LOG_WARNING, "SQLSetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01302                if (i > 10) {
01303                   ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01304                   break;
01305                }
01306             }
01307          }
01308       }
01309    } else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
01310       /* Non-pooled connections -- but must use a separate connection handle */
01311       if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
01312          ast_debug(1, "Object not found\n");
01313          obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
01314          if (!obj) {
01315             ao2_ref(class, -1);
01316             ast_debug(3, "Unable to allocate object\n");
01317             return NULL;
01318          }
01319          ast_mutex_init(&obj->lock);
01320          /* obj inherits the outstanding reference to class */
01321          obj->parent = class;
01322          class = NULL;
01323          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01324             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01325             ao2_ref(obj, -1);
01326             obj = NULL;
01327          } else {
01328             obj->used = 1;
01329             ao2_link(obj->parent->obj_container, obj);
01330             ast_atomic_fetchadd_int(&obj->parent->count, +1);
01331          }
01332       }
01333 
01334       if (!obj) {
01335          return NULL;
01336       }
01337 
01338       ast_mutex_lock(&obj->lock);
01339 
01340       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_OFF, 0) == SQL_ERROR) {
01341          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01342          for (i = 0; i < numfields; i++) {
01343             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01344             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01345             if (i > 10) {
01346                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01347                break;
01348             }
01349          }
01350       }
01351    } else {
01352       /* Non-pooled connection: multiple modules can use the same connection. */
01353       if ((obj = ao2_callback(class->obj_container, 0, aoro2_obj_notx_cb, NO_TX))) {
01354          /* Object is not constructed, so delete outstanding reference to class. */
01355          ast_assert(ao2_ref(class, 0) > 1);
01356          ao2_ref(class, -1);
01357          class = NULL;
01358       } else {
01359          /* No entry: build one */
01360          if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
01361             ast_assert(ao2_ref(class, 0) > 1);
01362             ao2_ref(class, -1);
01363             ast_debug(3, "Unable to allocate object\n");
01364             return NULL;
01365          }
01366          ast_mutex_init(&obj->lock);
01367          /* obj inherits the outstanding reference to class */
01368          obj->parent = class;
01369          class = NULL;
01370          if (odbc_obj_connect(obj) == ODBC_FAIL) {
01371             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
01372             ao2_ref(obj, -1);
01373             obj = NULL;
01374          } else {
01375             ao2_link(obj->parent->obj_container, obj);
01376             ast_assert(ao2_ref(obj, 0) > 1);
01377          }
01378       }
01379 
01380       if (!obj) {
01381          return NULL;
01382       }
01383 
01384       ast_mutex_lock(&obj->lock);
01385 
01386       if (SQLSetConnectAttr(obj->con, SQL_ATTR_AUTOCOMMIT, (void *)SQL_AUTOCOMMIT_ON, 0) == SQL_ERROR) {
01387          SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01388          for (i = 0; i < numfields; i++) {
01389             SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01390             ast_log(LOG_WARNING, "SetConnectAttr (Autocommit) returned an error: %s: %s\n", state, diagnostic);
01391             if (i > 10) {
01392                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01393                break;
01394             }
01395          }
01396       }
01397    }
01398 
01399    ast_assert(obj != NULL);
01400 
01401    /* Set the isolation property */
01402    if (SQLSetConnectAttr(obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)obj->parent->isolation, 0) == SQL_ERROR) {
01403       SQLGetDiagField(SQL_HANDLE_DBC, obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01404       for (i = 0; i < numfields; i++) {
01405          SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01406          ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01407          if (i > 10) {
01408             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01409             break;
01410          }
01411       }
01412    }
01413 
01414    if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
01415       /* Check if this connection qualifies for reconnection, with negative connection cache time */
01416       if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
01417          odbc_obj_connect(obj);
01418       }
01419    } else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
01420       ast_odbc_sanity_check(obj);
01421    } else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
01422       odbc_obj_connect(obj);
01423    }
01424 
01425 #ifdef DEBUG_THREADS
01426    ast_copy_string(obj->file, file, sizeof(obj->file));
01427    ast_copy_string(obj->function, function, sizeof(obj->function));
01428    obj->lineno = lineno;
01429 #endif
01430 
01431    /* We had it locked because of the obj_connects we see here. */
01432    ast_mutex_unlock(&obj->lock);
01433 
01434    ast_assert(class == NULL);
01435 
01436    ast_assert(ao2_ref(obj, 0) > 1);
01437    return obj;
01438 }
01439 
01440 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
01441 {
01442    struct ast_flags flags = { check ? RES_ODBC_SANITY_CHECK : 0 };
01443    return _ast_odbc_request_obj2(name, flags, file, function, lineno);
01444 }
01445 
01446 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
01447 {
01448    struct ast_datastore *txn_store;
01449    AST_LIST_HEAD(, odbc_txn_frame) *oldlist;
01450    struct odbc_txn_frame *txn = NULL;
01451 
01452    if (!chan) {
01453       /* No channel == no transaction */
01454       return NULL;
01455    }
01456 
01457    ast_channel_lock(chan);
01458    if ((txn_store = ast_channel_datastore_find(chan, &txn_info, NULL))) {
01459       oldlist = txn_store->data;
01460    } else {
01461       ast_channel_unlock(chan);
01462       return NULL;
01463    }
01464 
01465    AST_LIST_LOCK(oldlist);
01466    ast_channel_unlock(chan);
01467 
01468    AST_LIST_TRAVERSE(oldlist, txn, list) {
01469       if (txn->obj && txn->obj->parent && !strcmp(txn->obj->parent->name, objname)) {
01470          AST_LIST_UNLOCK(oldlist);
01471          return txn->obj;
01472       }
01473    }
01474    AST_LIST_UNLOCK(oldlist);
01475    return NULL;
01476 }
01477 
01478 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
01479 {
01480    int res;
01481    SQLINTEGER err;
01482    short int mlen;
01483    unsigned char msg[200], state[10];
01484    SQLHDBC con;
01485 
01486    /* Nothing to disconnect */
01487    if (!obj->con) {
01488       return ODBC_SUCCESS;
01489    }
01490 
01491    con = obj->con;
01492    obj->con = NULL;
01493    res = SQLDisconnect(con);
01494 
01495    if (obj->parent) {
01496       if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
01497          ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
01498       } else {
01499          ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
01500       }
01501    }
01502 
01503    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) == SQL_SUCCESS)) {
01504       ast_log(LOG_DEBUG, "Database handle %p deallocated\n", con);
01505    } else {
01506       SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01507       ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01508    }
01509 
01510    obj->up = 0;
01511    return ODBC_SUCCESS;
01512 }
01513 
01514 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
01515 {
01516    int res;
01517    SQLINTEGER err;
01518    short int mlen;
01519    unsigned char msg[200], state[10];
01520 #ifdef NEEDTRACE
01521    SQLINTEGER enable = 1;
01522    char *tracefile = "/tmp/odbc.trace";
01523 #endif
01524    SQLHDBC con;
01525 
01526    if (obj->up) {
01527       odbc_obj_disconnect(obj);
01528       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
01529    } else {
01530       ast_assert(obj->con == NULL);
01531       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
01532    }
01533 
01534    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
01535 
01536    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01537       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
01538       obj->parent->last_negative_connect = ast_tvnow();
01539       return ODBC_FAIL;
01540    }
01541    SQLSetConnectAttr(con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01542    SQLSetConnectAttr(con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)(long) obj->parent->conntimeout, 0);
01543 #ifdef NEEDTRACE
01544    SQLSetConnectAttr(con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
01545    SQLSetConnectAttr(con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
01546 #endif
01547 
01548    res = SQLConnect(con,
01549          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
01550          (SQLCHAR *) obj->parent->username, SQL_NTS,
01551          (SQLCHAR *) obj->parent->password, SQL_NTS);
01552 
01553    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01554       SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01555       obj->parent->last_negative_connect = ast_tvnow();
01556       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
01557       if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con) != SQL_SUCCESS)) {
01558          SQLGetDiagRec(SQL_HANDLE_DBC, con, 1, state, &err, msg, 100, &mlen);
01559          ast_log(LOG_WARNING, "Unable to deallocate database handle %p? %d errno=%d %s\n", con, res, (int)err, msg);
01560       }
01561       return ODBC_FAIL;
01562    } else {
01563       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
01564       obj->up = 1;
01565       obj->last_used = ast_tvnow();
01566    }
01567 
01568    obj->con = con;
01569    return ODBC_SUCCESS;
01570 }
01571 
01572 static int acf_transaction_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01573 {
01574    AST_DECLARE_APP_ARGS(args,
01575       AST_APP_ARG(property);
01576       AST_APP_ARG(opt);
01577    );
01578    struct odbc_txn_frame *tx;
01579 
01580    AST_STANDARD_APP_ARGS(args, data);
01581    if (strcasecmp(args.property, "transaction") == 0) {
01582       if ((tx = find_transaction(chan, NULL, NULL, 1))) {
01583          ast_copy_string(buf, tx->name, len);
01584          return 0;
01585       }
01586    } else if (strcasecmp(args.property, "isolation") == 0) {
01587       if (!ast_strlen_zero(args.opt)) {
01588          tx = find_transaction(chan, NULL, args.opt, 0);
01589       } else {
01590          tx = find_transaction(chan, NULL, NULL, 1);
01591       }
01592       if (tx) {
01593          ast_copy_string(buf, isolation2text(tx->isolation), len);
01594          return 0;
01595       }
01596    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01597       if (!ast_strlen_zero(args.opt)) {
01598          tx = find_transaction(chan, NULL, args.opt, 0);
01599       } else {
01600          tx = find_transaction(chan, NULL, NULL, 1);
01601       }
01602       if (tx) {
01603          ast_copy_string(buf, tx->forcecommit ? "1" : "0", len);
01604          return 0;
01605       }
01606    }
01607    return -1;
01608 }
01609 
01610 static int acf_transaction_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
01611 {
01612    AST_DECLARE_APP_ARGS(args,
01613       AST_APP_ARG(property);
01614       AST_APP_ARG(opt);
01615    );
01616    struct odbc_txn_frame *tx;
01617    SQLINTEGER nativeerror=0, numfields=0;
01618    SQLSMALLINT diagbytes=0, i;
01619    unsigned char state[10], diagnostic[256];
01620 
01621    AST_STANDARD_APP_ARGS(args, s);
01622    if (strcasecmp(args.property, "transaction") == 0) {
01623       /* Set active transaction */
01624       struct odbc_obj *obj;
01625       if ((tx = find_transaction(chan, NULL, value, 0))) {
01626          mark_transaction_active(chan, tx);
01627       } else {
01628          /* No such transaction, create one */
01629          struct ast_flags flags = { RES_ODBC_INDEPENDENT_CONNECTION };
01630          if (ast_strlen_zero(args.opt) || !(obj = ast_odbc_request_obj2(args.opt, flags))) {
01631             ast_log(LOG_ERROR, "Could not create transaction: invalid database specification '%s'\n", S_OR(args.opt, ""));
01632             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_DB");
01633             return -1;
01634          }
01635          if (!find_transaction(chan, obj, value, 0)) {
01636             pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01637             return -1;
01638          }
01639          obj->tx = 1;
01640       }
01641       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01642       return 0;
01643    } else if (strcasecmp(args.property, "forcecommit") == 0) {
01644       /* Set what happens when an uncommitted transaction ends without explicit Commit or Rollback */
01645       if (ast_strlen_zero(args.opt)) {
01646          tx = find_transaction(chan, NULL, NULL, 1);
01647       } else {
01648          tx = find_transaction(chan, NULL, args.opt, 0);
01649       }
01650       if (!tx) {
01651          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01652          return -1;
01653       }
01654       if (ast_true(value)) {
01655          tx->forcecommit = 1;
01656       } else if (ast_false(value)) {
01657          tx->forcecommit = 0;
01658       } else {
01659          ast_log(LOG_ERROR, "Invalid value for forcecommit: '%s'\n", S_OR(value, ""));
01660          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01661          return -1;
01662       }
01663 
01664       pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01665       return 0;
01666    } else if (strcasecmp(args.property, "isolation") == 0) {
01667       /* How do uncommitted transactions affect reads? */
01668       int isolation = text2isolation(value);
01669       if (ast_strlen_zero(args.opt)) {
01670          tx = find_transaction(chan, NULL, NULL, 1);
01671       } else {
01672          tx = find_transaction(chan, NULL, args.opt, 0);
01673       }
01674       if (!tx) {
01675          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "FAILED_TO_CREATE");
01676          return -1;
01677       }
01678       if (isolation == 0) {
01679          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "INVALID_VALUE");
01680          ast_log(LOG_ERROR, "Invalid isolation specification: '%s'\n", S_OR(value, ""));
01681       } else if (SQLSetConnectAttr(tx->obj->con, SQL_ATTR_TXN_ISOLATION, (void *)(long)isolation, 0) == SQL_ERROR) {
01682          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "SQL_ERROR");
01683          SQLGetDiagField(SQL_HANDLE_DBC, tx->obj->con, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
01684          for (i = 0; i < numfields; i++) {
01685             SQLGetDiagRec(SQL_HANDLE_DBC, tx->obj->con, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
01686             ast_log(LOG_WARNING, "SetConnectAttr (Txn isolation) returned an error: %s: %s\n", state, diagnostic);
01687             if (i > 10) {
01688                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
01689                break;
01690             }
01691          }
01692       } else {
01693          pbx_builtin_setvar_helper(chan, "ODBC_RESULT", "OK");
01694          tx->isolation = isolation;
01695       }
01696       return 0;
01697    } else {
01698       ast_log(LOG_ERROR, "Unknown property: '%s'\n", args.property);
01699       return -1;
01700    }
01701 }
01702 
01703 static struct ast_custom_function odbc_function = {
01704    .name = "ODBC",
01705    .read = acf_transaction_read,
01706    .write = acf_transaction_write,
01707 };
01708 
01709 static const char * const app_commit = "ODBC_Commit";
01710 static const char * const app_rollback = "ODBC_Rollback";
01711 
01712 /*!
01713  * \internal
01714  * \brief Implements the channels provider.
01715  */
01716 static int data_odbc_provider_handler(const struct ast_data_search *search,
01717       struct ast_data *root)
01718 {
01719    struct ao2_iterator aoi, aoi2;
01720    struct odbc_class *class;
01721    struct odbc_obj *current;
01722    struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection;
01723    struct ast_data *enum_node;
01724    int count;
01725 
01726    aoi = ao2_iterator_init(class_container, 0);
01727    while ((class = ao2_iterator_next(&aoi))) {
01728       data_odbc_class = ast_data_add_node(root, "class");
01729       if (!data_odbc_class) {
01730          ao2_ref(class, -1);
01731          continue;
01732       }
01733 
01734       ast_data_add_structure(odbc_class, data_odbc_class, class);
01735 
01736       if (!ao2_container_count(class->obj_container)) {
01737          ao2_ref(class, -1);
01738          continue;
01739       }
01740 
01741       data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
01742       if (!data_odbc_connections) {
01743          ao2_ref(class, -1);
01744          continue;
01745       }
01746 
01747       ast_data_add_bool(data_odbc_class, "shared", !class->haspool);
01748       /* isolation */
01749       enum_node = ast_data_add_node(data_odbc_class, "isolation");
01750       if (!enum_node) {
01751          ao2_ref(class, -1);
01752          continue;
01753       }
01754       ast_data_add_int(enum_node, "value", class->isolation);
01755       ast_data_add_str(enum_node, "text", isolation2text(class->isolation));
01756 
01757       count = 0;
01758       aoi2 = ao2_iterator_init(class->obj_container, 0);
01759       while ((current = ao2_iterator_next(&aoi2))) {
01760          data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection");
01761          if (!data_odbc_connection) {
01762             ao2_ref(current, -1);
01763             continue;
01764          }
01765 
01766          ast_mutex_lock(&current->lock);
01767          ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" :
01768                current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
01769          ast_data_add_bool(data_odbc_connection, "transactional", current->tx);
01770          ast_mutex_unlock(&current->lock);
01771 
01772          if (class->haspool) {
01773             ast_data_add_int(data_odbc_connection, "number", ++count);
01774          }
01775 
01776          ao2_ref(current, -1);
01777       }
01778       ao2_iterator_destroy(&aoi2);
01779       ao2_ref(class, -1);
01780 
01781       if (!ast_data_search_match(search, data_odbc_class)) {
01782          ast_data_remove_node(root, data_odbc_class);
01783       }
01784    }
01785    ao2_iterator_destroy(&aoi);
01786    return 0;
01787 }
01788 
01789 /*!
01790  * \internal
01791  * \brief /asterisk/res/odbc/listprovider.
01792  */
01793 static const struct ast_data_handler odbc_provider = {
01794    .version = AST_DATA_HANDLER_VERSION,
01795    .get = data_odbc_provider_handler
01796 };
01797 
01798 static const struct ast_data_entry odbc_providers[] = {
01799    AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
01800 };
01801 
01802 static int reload(void)
01803 {
01804    struct odbc_cache_tables *table;
01805    struct odbc_class *class;
01806    struct odbc_obj *current;
01807    struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
01808 
01809    /* First, mark all to be purged */
01810    while ((class = ao2_iterator_next(&aoi))) {
01811       class->delme = 1;
01812       ao2_ref(class, -1);
01813    }
01814    ao2_iterator_destroy(&aoi);
01815 
01816    load_odbc_config();
01817 
01818    /* Purge remaining classes */
01819 
01820    /* Note on how this works; this is a case of circular references, so we
01821     * explicitly do NOT want to use a callback here (or we wind up in
01822     * recursive hell).
01823     *
01824     * 1. Iterate through all the classes.  Note that the classes will currently
01825     * contain two classes of the same name, one of which is marked delme and
01826     * will be purged when all remaining objects of the class are released, and
01827     * the other, which was created above when we re-parsed the config file.
01828     * 2. On each class, there is a reference held by the master container and
01829     * a reference held by each connection object.  There are two cases for
01830     * destruction of the class, noted below.  However, in all cases, all O-refs
01831     * (references to objects) will first be freed, which will cause the C-refs
01832     * (references to classes) to be decremented (but never to 0, because the
01833     * class container still has a reference).
01834     *    a) If the class has outstanding objects, the C-ref by the class
01835     *    container will then be freed, which leaves only C-refs by any
01836     *    outstanding objects.  When the final outstanding object is released
01837     *    (O-refs held by applications and dialplan functions), it will in turn
01838     *    free the final C-ref, causing class destruction.
01839     *    b) If the class has no outstanding objects, when the class container
01840     *    removes the final C-ref, the class will be destroyed.
01841     */
01842    aoi = ao2_iterator_init(class_container, 0);
01843    while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
01844       if (class->delme) {
01845          struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
01846          while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
01847             ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
01848             ao2_ref(current, -1); /* O-ref-- (by iterator) */
01849             /* At this point, either
01850              * a) there's an outstanding O-ref, or
01851              * b) the object has already been destroyed.
01852              */
01853          }
01854          ao2_iterator_destroy(&aoi2);
01855          ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
01856          /* At this point, either
01857           * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
01858           * b) the last remaining C-ref is held by the iterator, which will be
01859           * destroyed in the next step.
01860           */
01861       }
01862       ao2_ref(class, -1); /* C-ref-- (by iterator) */
01863    }
01864    ao2_iterator_destroy(&aoi);
01865 
01866    /* Empty the cache; it will get rebuilt the next time the tables are needed. */
01867    AST_RWLIST_WRLOCK(&odbc_tables);
01868    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
01869       destroy_table_cache(table);
01870    }
01871    AST_RWLIST_UNLOCK(&odbc_tables);
01872 
01873    return 0;
01874 }
01875 
01876 static int unload_module(void)
01877 {
01878    /* Prohibit unloading */
01879    return -1;
01880 }
01881 
01882 static int load_module(void)
01883 {
01884    if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
01885       return AST_MODULE_LOAD_DECLINE;
01886    if (load_odbc_config() == -1)
01887       return AST_MODULE_LOAD_DECLINE;
01888    ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
01889    ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers));
01890    ast_register_application_xml(app_commit, commit_exec);
01891    ast_register_application_xml(app_rollback, rollback_exec);
01892    ast_custom_function_register(&odbc_function);
01893    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
01894    return 0;
01895 }
01896 
01897 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "ODBC resource",
01898       .load = load_module,
01899       .unload = unload_module,
01900       .reload = reload,
01901       .load_pri = AST_MODPRI_REALTIME_DEPEND,
01902           );

Generated on Mon Oct 8 12:39:05 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7