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

Generated on Mon Mar 19 11:30:29 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7