Sat Apr 12 07:12:54 2008

Asterisk developer's documentation


res_odbc.c File Reference

ODBC resource manager. More...

#include "asterisk.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/options.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"

Include dependency graph for res_odbc.c:

Go to the source code of this file.

Data Structures

struct  odbc_class

Functions

 AST_LIST_HEAD_STATIC (odbc_list, odbc_class)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,"ODBC Resource",.load=load_module,.unload=unload_module,.reload=reload,)
int ast_odbc_backslash_is_escape (struct odbc_obj *obj)
 Checks if the database natively supports backslash as an escape character.
SQLHSTMT ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
 Prepares, executes, and returns the resulting statement handle.
void ast_odbc_release_obj (struct odbc_obj *obj)
 Releases an ODBC object previously allocated by odbc_request_obj().
struct odbc_objast_odbc_request_obj (const char *name, int check)
 Retrieves a connected ODBC object.
int ast_odbc_sanity_check (struct odbc_obj *obj)
 Checks an ODBC object to ensure it is still connected.
int ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt)
 Executes a prepared statement handle.
static int load_module (void)
static int load_odbc_config (void)
static odbc_status odbc_obj_connect (struct odbc_obj *obj)
static odbc_status odbc_obj_disconnect (struct odbc_obj *obj)
static int odbc_register_class (struct odbc_class *class, int connect)
static int odbc_show_command (int fd, int argc, char **argv)
static int reload (void)
static int unload_module (void)

Variables

static struct ast_cli_entry cli_odbc []
static char show_usage []


Detailed Description

ODBC resource manager.

Author:
Mark Spencer <markster@digium.com>

Anthony Minessale II <anthmct@yahoo.com>

Definition in file res_odbc.c.


Function Documentation

AST_LIST_HEAD_STATIC ( odbc_list  ,
odbc_class   
)

AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_GLOBAL_SYMBOLS  ,
"ODBC Resource"  ,
load = load_module,
unload = unload_module,
reload = reload 
)

int ast_odbc_backslash_is_escape ( struct odbc_obj obj  ) 

Checks if the database natively supports backslash as an escape character.

Parameters:
obj The ODBC object
Returns:
Returns 1 if an ESCAPE clause is needed to support '\', 0 otherwise

Definition at line 388 of file res_odbc.c.

References odbc_obj::parent.

Referenced by realtime_multi_odbc(), and realtime_odbc().

00389 {
00390    return obj->parent->backslash_is_escape;
00391 }

SQLHSTMT ast_odbc_prepare_and_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  prepare_cb,
void *  data 
)

Prepares, executes, and returns the resulting statement handle.

Parameters:
obj The ODBC object
prepare_cb A function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound.
data A parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared.
Returns:
Returns a statement handle or NULL on error.

Definition at line 80 of file res_odbc.c.

References ast_log(), LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), realtime_multi_odbc(), realtime_odbc(), and update_odbc().

00081 {
00082    int res = 0, i, attempt;
00083    SQLINTEGER nativeerror=0, numfields=0;
00084    SQLSMALLINT diagbytes=0;
00085    unsigned char state[10], diagnostic[256];
00086    SQLHSTMT stmt;
00087 
00088    for (attempt = 0; attempt < 2; attempt++) {
00089       /* This prepare callback may do more than just prepare -- it may also
00090        * bind parameters, bind results, etc.  The real key, here, is that
00091        * when we disconnect, all handles become invalid for most databases.
00092        * We must therefore redo everything when we establish a new
00093        * connection. */
00094       stmt = prepare_cb(obj, data);
00095 
00096       if (stmt) {
00097          res = SQLExecute(stmt);
00098          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00099             if (res == SQL_ERROR) {
00100                SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00101                for (i = 0; i < numfields; i++) {
00102                   SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00103                   ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00104                   if (i > 10) {
00105                      ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00106                      break;
00107                   }
00108                }
00109             }
00110 
00111             ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00112             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00113             stmt = NULL;
00114 
00115             obj->up = 0;
00116             /*
00117              * While this isn't the best way to try to correct an error, this won't automatically
00118              * fail when the statement handle invalidates.
00119              */
00120             /* XXX Actually, it might, if we're using a non-pooled connection. Possible race here. XXX */
00121             odbc_obj_disconnect(obj);
00122             odbc_obj_connect(obj);
00123             continue;
00124          }
00125          break;
00126       } else {
00127          ast_log(LOG_WARNING, "SQL Prepare failed.  Attempting a reconnect...\n");
00128          odbc_obj_disconnect(obj);
00129          odbc_obj_connect(obj);
00130       }
00131    }
00132 
00133    return stmt;
00134 }

void ast_odbc_release_obj ( struct odbc_obj obj  ) 

Releases an ODBC object previously allocated by odbc_request_obj().

Parameters:
obj The ODBC object

Definition at line 381 of file res_odbc.c.

References odbc_obj::used.

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), and update_odbc().

00382 {
00383    /* For pooled connections, this frees the connection to be
00384     * reused.  For non-pooled connections, it does nothing. */
00385    obj->used = 0;
00386 }

struct odbc_obj* ast_odbc_request_obj ( const char *  name,
int  check 
) [read]

Retrieves a connected ODBC object.

Parameters:
name The name of the ODBC class for which a connection is needed.
check Whether to ensure that a connection is valid before returning the handle. Usually unnecessary.
Returns:
Returns an ODBC object or NULL if there is no connection available with the requested name.
Connection classes may, in fact, contain multiple connection handles. If the connection is pooled, then each connection will be dedicated to the thread which requests it. Note that all connections should be released when the thread is done by calling odbc_release_obj(), below.

Definition at line 393 of file res_odbc.c.

References ast_calloc, AST_LIST_INSERT_HEAD, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_odbc_sanity_check(), free, odbc_obj::lock, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), odbc_obj::parent, and odbc_obj::used.

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), and update_odbc().

00394 {
00395    struct odbc_obj *obj = NULL;
00396    struct odbc_class *class;
00397 
00398    AST_LIST_LOCK(&odbc_list);
00399    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00400       if (!strcmp(class->name, name))
00401          break;
00402    }
00403    AST_LIST_UNLOCK(&odbc_list);
00404 
00405    if (!class)
00406       return NULL;
00407 
00408    AST_LIST_LOCK(&class->odbc_obj);
00409    if (class->haspool) {
00410       /* Recycle connections before building another */
00411       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00412          if (! obj->used) {
00413             obj->used = 1;
00414             break;
00415          }
00416       }
00417 
00418       if (!obj && (class->count < class->limit)) {
00419          class->count++;
00420          obj = ast_calloc(1, sizeof(*obj));
00421          if (!obj) {
00422             AST_LIST_UNLOCK(&class->odbc_obj);
00423             return NULL;
00424          }
00425          ast_mutex_init(&obj->lock);
00426          obj->parent = class;
00427          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00428             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00429             ast_mutex_destroy(&obj->lock);
00430             free(obj);
00431             obj = NULL;
00432             class->count--;
00433          } else {
00434             obj->used = 1;
00435             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00436          }
00437       }
00438    } else {
00439       /* Non-pooled connection: multiple modules can use the same connection. */
00440       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00441          /* Non-pooled connection: if there is an entry, return it */
00442          break;
00443       }
00444 
00445       if (!obj) {
00446          /* No entry: build one */
00447          obj = ast_calloc(1, sizeof(*obj));
00448          if (!obj) {
00449             AST_LIST_UNLOCK(&class->odbc_obj);
00450             return NULL;
00451          }
00452          ast_mutex_init(&obj->lock);
00453          obj->parent = class;
00454          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00455             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00456             ast_mutex_destroy(&obj->lock);
00457             free(obj);
00458             obj = NULL;
00459          } else {
00460             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00461          }
00462       }
00463    }
00464    AST_LIST_UNLOCK(&class->odbc_obj);
00465 
00466    if (obj && check) {
00467       ast_odbc_sanity_check(obj);
00468    }
00469    return obj;
00470 }

int ast_odbc_sanity_check ( struct odbc_obj obj  ) 

Checks an ODBC object to ensure it is still connected.

Parameters:
obj The ODBC object
Returns:
Returns 0 if connected, -1 otherwise.

Definition at line 178 of file res_odbc.c.

References ast_log(), odbc_obj::con, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.

Referenced by ast_odbc_request_obj(), and odbc_show_command().

00179 {
00180    char *test_sql = "select 1";
00181    SQLHSTMT stmt;
00182    int res = 0;
00183 
00184    if (obj->up) {
00185       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00186       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00187          obj->up = 0;
00188       } else {
00189          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00190          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00191             obj->up = 0;
00192          } else {
00193             res = SQLExecute(stmt);
00194             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00195                obj->up = 0;
00196             }
00197          }
00198       }
00199       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00200    }
00201 
00202    if (!obj->up) { /* Try to reconnect! */
00203       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00204       odbc_obj_disconnect(obj);
00205       odbc_obj_connect(obj);
00206    }
00207    return obj->up;
00208 }

int ast_odbc_smart_execute ( struct odbc_obj obj,
SQLHSTMT  stmt 
)

Executes a prepared statement handle.

Parameters:
obj The non-NULL result of odbc_request_obj()
stmt The prepared statement handle
Returns:
Returns 0 on success or -1 on failure
This function was originally designed simply to execute a prepared statement handle and to retry if the initial execution failed. Unfortunately, it did this by disconnecting and reconnecting the database handle which on most databases causes the statement handle to become invalid. Therefore, this method has been deprecated in favor of odbc_prepare_and_execute() which allows the statement to be prepared multiple times, if necessary, in case of a loss of connection.

This function really only ever worked with MySQL, where the statement handle is not prepared on the server. If you are not using MySQL, you should avoid it.

Definition at line 136 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::lock, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.

00137 {
00138    int res = 0, i;
00139    SQLINTEGER nativeerror=0, numfields=0;
00140    SQLSMALLINT diagbytes=0;
00141    unsigned char state[10], diagnostic[256];
00142 
00143    res = SQLExecute(stmt);
00144    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00145       if (res == SQL_ERROR) {
00146          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00147          for (i = 0; i < numfields; i++) {
00148             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00149             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00150             if (i > 10) {
00151                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00152                break;
00153             }
00154          }
00155       }
00156 #if 0
00157       /* This is a really bad method of trying to correct a dead connection.  It
00158        * only ever really worked with MySQL.  It will not work with any other
00159        * database, since most databases prepare their statements on the server,
00160        * and if you disconnect, you invalidate the statement handle.  Hence, if
00161        * you disconnect, you're going to fail anyway, whether you try to execute
00162        * a second time or not.
00163        */
00164       ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00165       ast_mutex_lock(&obj->lock);
00166       obj->up = 0;
00167       ast_mutex_unlock(&obj->lock);
00168       odbc_obj_disconnect(obj);
00169       odbc_obj_connect(obj);
00170       res = SQLExecute(stmt);
00171 #endif
00172    }
00173    
00174    return res;
00175 }

static int load_module ( void   )  [static]

Definition at line 695 of file res_odbc.c.

References ast_cli_register_multiple(), ast_log(), AST_MODULE_LOAD_DECLINE, load_odbc_config(), and LOG_NOTICE.

00696 {
00697    if(load_odbc_config() == -1)
00698       return AST_MODULE_LOAD_DECLINE;
00699    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00700    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00701    return 0;
00702 }

static int load_odbc_config ( void   )  [static]

Definition at line 210 of file res_odbc.c.

References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_false(), ast_log(), ast_strlen_zero(), ast_true(), ast_variable_browse(), config, dsn, enabled, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, odbc_register_class(), password, setenv(), username, and ast_variable::value.

Referenced by load_module().

00211 {
00212    static char *cfg = "res_odbc.conf";
00213    struct ast_config *config;
00214    struct ast_variable *v;
00215    char *cat, *dsn, *username, *password;
00216    int enabled, pooling, limit, bse;
00217    int connect = 0, res = 0;
00218 
00219    struct odbc_class *new;
00220 
00221    config = ast_config_load(cfg);
00222    if (!config) {
00223       ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00224       return -1;
00225    }
00226    for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00227       if (!strcasecmp(cat, "ENV")) {
00228          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00229             setenv(v->name, v->value, 1);
00230             ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00231          }
00232       } else {
00233          /* Reset all to defaults for each class of odbc connections */
00234          dsn = username = password = NULL;
00235          enabled = 1;
00236          connect = 0;
00237          pooling = 0;
00238          limit = 0;
00239          bse = 1;
00240          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00241             if (!strcasecmp(v->name, "pooling")) {
00242                if (ast_true(v->value))
00243                   pooling = 1;
00244             } else if (!strcasecmp(v->name, "limit")) {
00245                sscanf(v->value, "%d", &limit);
00246                if (ast_true(v->value) && !limit) {
00247                   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);
00248                   limit = 1023;
00249                } else if (ast_false(v->value)) {
00250                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00251                   enabled = 0;
00252                   break;
00253                }
00254             } else if (!strcasecmp(v->name, "enabled")) {
00255                enabled = ast_true(v->value);
00256             } else if (!strcasecmp(v->name, "pre-connect")) {
00257                connect = ast_true(v->value);
00258             } else if (!strcasecmp(v->name, "dsn")) {
00259                dsn = v->value;
00260             } else if (!strcasecmp(v->name, "username")) {
00261                username = v->value;
00262             } else if (!strcasecmp(v->name, "password")) {
00263                password = v->value;
00264             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00265                bse = ast_true(v->value);
00266             }
00267          }
00268 
00269          if (enabled && !ast_strlen_zero(dsn)) {
00270             new = ast_calloc(1, sizeof(*new));
00271 
00272             if (!new) {
00273                res = -1;
00274                break;
00275             }
00276 
00277             if (cat)
00278                ast_copy_string(new->name, cat, sizeof(new->name));
00279             if (dsn)
00280                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00281             if (username)
00282                ast_copy_string(new->username, username, sizeof(new->username));
00283             if (password)
00284                ast_copy_string(new->password, password, sizeof(new->password));
00285 
00286             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00287             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00288 
00289             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00290                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00291                SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00292                return res;
00293             }
00294 
00295             if (pooling) {
00296                new->haspool = pooling;
00297                if (limit) {
00298                   new->limit = limit;
00299                } else {
00300                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00301                   new->limit = 5;
00302                }
00303             }
00304 
00305             new->backslash_is_escape = bse ? 1 : 0;
00306 
00307             odbc_register_class(new, connect);
00308             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00309          }
00310       }
00311    }
00312    ast_config_destroy(config);
00313    return res;
00314 }

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 490 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, ODBC_FAIL, odbc_obj_disconnect(), ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.

Referenced by ast_odbc_prepare_and_execute(), ast_odbc_request_obj(), ast_odbc_sanity_check(), and ast_odbc_smart_execute().

00491 {
00492    int res;
00493    SQLINTEGER err;
00494    short int mlen;
00495    unsigned char msg[200], stat[10];
00496 #ifdef NEEDTRACE
00497    SQLINTEGER enable = 1;
00498    char *tracefile = "/tmp/odbc.trace";
00499 #endif
00500    ast_mutex_lock(&obj->lock);
00501 
00502    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00503 
00504    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00505       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00506       ast_mutex_unlock(&obj->lock);
00507       return ODBC_FAIL;
00508    }
00509    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00510 #ifdef NEEDTRACE
00511    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00512    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00513 #endif
00514 
00515    if (obj->up) {
00516       odbc_obj_disconnect(obj);
00517       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00518    } else {
00519       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00520    }
00521 
00522    res = SQLConnect(obj->con,
00523          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00524          (SQLCHAR *) obj->parent->username, SQL_NTS,
00525          (SQLCHAR *) obj->parent->password, SQL_NTS);
00526 
00527    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00528       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00529       ast_mutex_unlock(&obj->lock);
00530       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00531       return ODBC_FAIL;
00532    } else {
00533       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00534       obj->up = 1;
00535    }
00536 
00537    ast_mutex_unlock(&obj->lock);
00538    return ODBC_SUCCESS;
00539 }

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 472 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_obj::lock, LOG_WARNING, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.

Referenced by ast_odbc_prepare_and_execute(), ast_odbc_sanity_check(), ast_odbc_smart_execute(), odbc_obj_connect(), and reload().

00473 {
00474    int res;
00475    ast_mutex_lock(&obj->lock);
00476 
00477    res = SQLDisconnect(obj->con);
00478 
00479    if (res == ODBC_SUCCESS) {
00480       ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00481    } else {
00482       ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n",
00483       obj->parent->name, obj->parent->dsn);
00484    }
00485    obj->up = 0;
00486    ast_mutex_unlock(&obj->lock);
00487    return ODBC_SUCCESS;
00488 }

static int odbc_register_class ( struct odbc_class class,
int  connect 
) [static]

Definition at line 359 of file res_odbc.c.

References AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj(), and LOG_WARNING.

Referenced by load_odbc_config(), and reload().

00360 {
00361    struct odbc_obj *obj;
00362    if (class) {
00363       AST_LIST_LOCK(&odbc_list);
00364       AST_LIST_INSERT_HEAD(&odbc_list, class, list);
00365       AST_LIST_UNLOCK(&odbc_list);
00366 
00367       if (connect) {
00368          /* Request and release builds a connection */
00369          obj = ast_odbc_request_obj(class->name, 0);
00370          if (obj)
00371             ast_odbc_release_obj(obj);
00372       }
00373 
00374       return 0;
00375    } else {
00376       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00377       return -1;
00378    }
00379 }

static int odbc_show_command ( int  fd,
int  argc,
char **  argv 
) [static]

Definition at line 316 of file res_odbc.c.

References ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_odbc_sanity_check(), odbc_obj::up, and odbc_obj::used.

00317 {
00318    struct odbc_class *class;
00319    struct odbc_obj *current;
00320 
00321    AST_LIST_LOCK(&odbc_list);
00322    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00323       if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) {
00324          int count = 0;
00325          ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn);
00326 
00327          if (class->haspool) {
00328             ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count);
00329 
00330             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00331                ast_cli(fd, "  Connection %d: %s\n", ++count, current->used ? "in use" : current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00332             }
00333          } else {
00334             /* Should only ever be one of these */
00335             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00336                ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no");
00337             }
00338          }
00339 
00340             ast_cli(fd, "\n");
00341       }
00342    }
00343    AST_LIST_UNLOCK(&odbc_list);
00344 
00345    return 0;
00346 }

static int reload ( void   )  [static]

Definition at line 541 of file res_odbc.c.

References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_false(), AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy(), ast_strlen_zero(), ast_true(), ast_variable_browse(), config, dsn, enabled, free, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, odbc_obj_disconnect(), odbc_register_class(), password, setenv(), username, and ast_variable::value.

00542 {
00543    static char *cfg = "res_odbc.conf";
00544    struct ast_config *config;
00545    struct ast_variable *v;
00546    char *cat, *dsn, *username, *password;
00547    int enabled, pooling, limit, bse;
00548    int connect = 0, res = 0;
00549 
00550    struct odbc_class *new, *class;
00551    struct odbc_obj *current;
00552 
00553    /* First, mark all to be purged */
00554    AST_LIST_LOCK(&odbc_list);
00555    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00556       class->delme = 1;
00557    }
00558 
00559    config = ast_config_load(cfg);
00560    if (config) {
00561       for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00562          if (!strcasecmp(cat, "ENV")) {
00563             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00564                setenv(v->name, v->value, 1);
00565                ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00566             }
00567          } else {
00568             /* Reset all to defaults for each class of odbc connections */
00569             dsn = username = password = NULL;
00570             enabled = 1;
00571             connect = 0;
00572             pooling = 0;
00573             limit = 0;
00574             bse = 1;
00575             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00576                if (!strcasecmp(v->name, "pooling")) {
00577                   pooling = 1;
00578                } else if (!strcasecmp(v->name, "limit")) {
00579                   sscanf(v->value, "%d", &limit);
00580                   if (ast_true(v->value) && !limit) {
00581                      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);
00582                      limit = 1023;
00583                   } else if (ast_false(v->value)) {
00584                      ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00585                      enabled = 0;
00586                      break;
00587                   }
00588                } else if (!strcasecmp(v->name, "enabled")) {
00589                   enabled = ast_true(v->value);
00590                } else if (!strcasecmp(v->name, "pre-connect")) {
00591                   connect = ast_true(v->value);
00592                } else if (!strcasecmp(v->name, "dsn")) {
00593                   dsn = v->value;
00594                } else if (!strcasecmp(v->name, "username")) {
00595                   username = v->value;
00596                } else if (!strcasecmp(v->name, "password")) {
00597                   password = v->value;
00598                } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00599                   bse = ast_true(v->value);
00600                }
00601             }
00602 
00603             if (enabled && !ast_strlen_zero(dsn)) {
00604                /* First, check the list to see if it already exists */
00605                AST_LIST_TRAVERSE(&odbc_list, class, list) {
00606                   if (!strcmp(class->name, cat)) {
00607                      class->delme = 0;
00608                      break;
00609                   }
00610                }
00611 
00612                if (class) {
00613                   new = class;
00614                } else {
00615                   new = ast_calloc(1, sizeof(*new));
00616                }
00617 
00618                if (!new) {
00619                   res = -1;
00620                   break;
00621                }
00622 
00623                if (cat)
00624                   ast_copy_string(new->name, cat, sizeof(new->name));
00625                if (dsn)
00626                   ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00627                if (username)
00628                   ast_copy_string(new->username, username, sizeof(new->username));
00629                if (password)
00630                   ast_copy_string(new->password, password, sizeof(new->password));
00631 
00632                if (!class) {
00633                   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00634                   res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00635 
00636                   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00637                      ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00638                      SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00639                      AST_LIST_UNLOCK(&odbc_list);
00640                      return res;
00641                   }
00642                }
00643 
00644                if (pooling) {
00645                   new->haspool = pooling;
00646                   if (limit) {
00647                      new->limit = limit;
00648                   } else {
00649                      ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00650                      new->limit = 5;
00651                   }
00652                }
00653 
00654                new->backslash_is_escape = bse;
00655 
00656                if (class) {
00657                   ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
00658                } else {
00659                   odbc_register_class(new, connect);
00660                   ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00661                }
00662             }
00663          }
00664       }
00665       ast_config_destroy(config);
00666    }
00667 
00668    /* Purge classes that we know can go away (pooled with 0, only) */
00669    AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
00670       if (class->delme && class->haspool && class->count == 0) {
00671          AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) {
00672             AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list);
00673             odbc_obj_disconnect(current);
00674             ast_mutex_destroy(&current->lock);
00675             free(current);
00676          }
00677          AST_LIST_TRAVERSE_SAFE_END;
00678 
00679          AST_LIST_REMOVE_CURRENT(&odbc_list, list);
00680          free(class);
00681       }
00682    }
00683    AST_LIST_TRAVERSE_SAFE_END;
00684    AST_LIST_UNLOCK(&odbc_list);
00685 
00686    return 0;
00687 }

static int unload_module ( void   )  [static]

Definition at line 689 of file res_odbc.c.

00690 {
00691    /* Prohibit unloading */
00692    return -1;
00693 }


Variable Documentation

struct ast_cli_entry cli_odbc[] [static]

Initial value:

 {
   { { "odbc", "show", NULL },
   odbc_show_command, "List ODBC DSN(s)",
   show_usage },
}

Definition at line 353 of file res_odbc.c.

char show_usage[] [static]

Initial value:

"Usage: odbc show [<class>]\n"
"       List settings of a particular ODBC class.\n"
"       or, if not specified, all classes.\n"

Definition at line 348 of file res_odbc.c.


Generated on Sat Apr 12 07:12:54 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5