Thu Apr 28 2011 17:13:35

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