Mon Nov 24 15:34:49 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 "asterisk/time.h"

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().
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 backslash is a native escape character, 0 if an ESCAPE clause is needed to support '\'

Definition at line 396 of file res_odbc.c.

References odbc_obj::parent.

Referenced by realtime_multi_odbc(), and realtime_odbc().

00397 {
00398    return obj->parent->backslash_is_escape;
00399 }

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 82 of file res_odbc.c.

References ast_log(), LOG_WARNING, odbc_obj_connect(), and odbc_obj_disconnect().

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

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

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 389 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().

00390 {
00391    /* For pooled connections, this frees the connection to be
00392     * reused.  For non-pooled connections, it does nothing. */
00393    obj->used = 0;
00394 }

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

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 401 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::last_used, 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().

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

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 182 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().

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

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 139 of file res_odbc.c.

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

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

static int load_module ( void   )  [static]

Definition at line 722 of file res_odbc.c.

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

00723 {
00724    if(load_odbc_config() == -1)
00725       return AST_MODULE_LOAD_DECLINE;
00726    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00727    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00728    return 0;
00729 }

static int load_odbc_config ( void   )  [static]

Definition at line 214 of file res_odbc.c.

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

Referenced by load_module().

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

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 511 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_obj::last_used, 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().

00512 {
00513    int res;
00514    SQLINTEGER err;
00515    short int mlen;
00516    unsigned char msg[200], stat[10];
00517 #ifdef NEEDTRACE
00518    SQLINTEGER enable = 1;
00519    char *tracefile = "/tmp/odbc.trace";
00520 #endif
00521    ast_mutex_lock(&obj->lock);
00522 
00523    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00524 
00525    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00526       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00527       ast_mutex_unlock(&obj->lock);
00528       return ODBC_FAIL;
00529    }
00530    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00531    SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00532 #ifdef NEEDTRACE
00533    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00534    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00535 #endif
00536 
00537    if (obj->up) {
00538       odbc_obj_disconnect(obj);
00539       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00540    } else {
00541       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00542    }
00543 
00544    res = SQLConnect(obj->con,
00545          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00546          (SQLCHAR *) obj->parent->username, SQL_NTS,
00547          (SQLCHAR *) obj->parent->password, SQL_NTS);
00548 
00549    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00550       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00551       ast_mutex_unlock(&obj->lock);
00552       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00553       return ODBC_FAIL;
00554    } else {
00555       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00556       obj->up = 1;
00557       obj->last_used = ast_tvnow();
00558    }
00559 
00560    ast_mutex_unlock(&obj->lock);
00561    return ODBC_SUCCESS;
00562 }

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 482 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_obj::lock, LOG_DEBUG, 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(), and odbc_obj_connect().

00483 {
00484    int res;
00485    SQLINTEGER err;
00486    short int mlen;
00487    unsigned char msg[200], stat[10];
00488 
00489    ast_mutex_lock(&obj->lock);
00490 
00491    res = SQLDisconnect(obj->con);
00492 
00493    if (res == ODBC_SUCCESS) {
00494       ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00495    } else {
00496       ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00497    }
00498 
00499    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == ODBC_SUCCESS)) {
00500       ast_log(LOG_DEBUG, "Database handle deallocated\n");
00501    } else {
00502       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00503       ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00504    }
00505 
00506    obj->up = 0;
00507    ast_mutex_unlock(&obj->lock);
00508    return ODBC_SUCCESS;
00509 }

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

Definition at line 367 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.

00368 {
00369    struct odbc_obj *obj;
00370    if (class) {
00371       AST_LIST_LOCK(&odbc_list);
00372       AST_LIST_INSERT_HEAD(&odbc_list, class, list);
00373       AST_LIST_UNLOCK(&odbc_list);
00374 
00375       if (connect) {
00376          /* Request and release builds a connection */
00377          obj = ast_odbc_request_obj(class->name, 0);
00378          if (obj)
00379             ast_odbc_release_obj(obj);
00380       }
00381 
00382       return 0;
00383    } else {
00384       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00385       return -1;
00386    }
00387 }

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

Definition at line 324 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.

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

static int reload ( void   )  [static]

Definition at line 564 of file res_odbc.c.

References ast_category_browse(), ast_config_load(), ast_false(), AST_LIST_LOCK, AST_LIST_TRAVERSE, ast_log(), ast_true(), ast_variable_browse(), config, enabled, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, setenv(), and ast_variable::value.

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

static int unload_module ( void   )  [static]

Definition at line 716 of file res_odbc.c.

00717 {
00718    /* Prohibit unloading */
00719    return -1;
00720 }


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 361 of file res_odbc.c.

Referenced by load_module().

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 356 of file res_odbc.c.


Generated on Mon Nov 24 15:34:49 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7