Wed Aug 7 17:15:44 2019

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

Generated on 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1