Wed Apr 6 11:29:47 2011

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

Generated on Wed Apr 6 11:29:47 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7