Wed Aug 15 01:25:30 2007

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,)
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 
)

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

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

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

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

References odbc_obj::used.

00376 {
00377    /* For pooled connections, this frees the connection to be
00378     * reused.  For non-pooled connections, it does nothing. */
00379    obj->used = 0;
00380 }

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 382 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.

00383 {
00384    struct odbc_obj *obj = NULL;
00385    struct odbc_class *class;
00386 
00387    AST_LIST_LOCK(&odbc_list);
00388    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00389       if (!strcmp(class->name, name))
00390          break;
00391    }
00392    AST_LIST_UNLOCK(&odbc_list);
00393 
00394    if (!class)
00395       return NULL;
00396 
00397    AST_LIST_LOCK(&class->odbc_obj);
00398    if (class->haspool) {
00399       /* Recycle connections before building another */
00400       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00401          if (! obj->used) {
00402             obj->used = 1;
00403             break;
00404          }
00405       }
00406 
00407       if (!obj && (class->count < class->limit)) {
00408          class->count++;
00409          obj = ast_calloc(1, sizeof(*obj));
00410          if (!obj) {
00411             AST_LIST_UNLOCK(&class->odbc_obj);
00412             return NULL;
00413          }
00414          ast_mutex_init(&obj->lock);
00415          obj->parent = class;
00416          odbc_obj_connect(obj);
00417          AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00418       }
00419    } else {
00420       /* Non-pooled connection: multiple modules can use the same connection. */
00421       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00422          /* Non-pooled connection: if there is an entry, return it */
00423          break;
00424       }
00425 
00426       if (!obj) {
00427          /* No entry: build one */
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          } else {
00441             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00442          }
00443       }
00444    }
00445    AST_LIST_UNLOCK(&class->odbc_obj);
00446 
00447    if (obj && check) {
00448       ast_odbc_sanity_check(obj);
00449    }
00450    return obj;
00451 }

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

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

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

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 135 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.

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

static int load_module ( void   )  [static]

Definition at line 671 of file res_odbc.c.

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

00672 {
00673    if(load_odbc_config() == -1)
00674       return AST_MODULE_LOAD_DECLINE;
00675    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00676    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00677    return 0;
00678 }

static int load_odbc_config ( void   )  [static]

Definition at line 209 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().

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

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 471 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().

00472 {
00473    int res;
00474    SQLINTEGER err;
00475    short int mlen;
00476    unsigned char msg[200], stat[10];
00477 #ifdef NEEDTRACE
00478    SQLINTEGER enable = 1;
00479    char *tracefile = "/tmp/odbc.trace";
00480 #endif
00481    ast_mutex_lock(&obj->lock);
00482 
00483    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00484 
00485    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00486       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00487       ast_mutex_unlock(&obj->lock);
00488       return ODBC_FAIL;
00489    }
00490    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00491 #ifdef NEEDTRACE
00492    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00493    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00494 #endif
00495 
00496    if (obj->up) {
00497       odbc_obj_disconnect(obj);
00498       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00499    } else {
00500       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00501    }
00502 
00503    res = SQLConnect(obj->con,
00504          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00505          (SQLCHAR *) obj->parent->username, SQL_NTS,
00506          (SQLCHAR *) obj->parent->password, SQL_NTS);
00507 
00508    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00509       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00510       ast_mutex_unlock(&obj->lock);
00511       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00512       return ODBC_FAIL;
00513    } else {
00514       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00515       obj->up = 1;
00516    }
00517 
00518    ast_mutex_unlock(&obj->lock);
00519    return ODBC_SUCCESS;
00520 }

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 453 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().

00454 {
00455    int res;
00456    ast_mutex_lock(&obj->lock);
00457 
00458    res = SQLDisconnect(obj->con);
00459 
00460    if (res == ODBC_SUCCESS) {
00461       ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00462    } else {
00463       ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n",
00464       obj->parent->name, obj->parent->dsn);
00465    }
00466    obj->up = 0;
00467    ast_mutex_unlock(&obj->lock);
00468    return ODBC_SUCCESS;
00469 }

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

Definition at line 353 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().

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

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

Definition at line 310 of file res_odbc.c.

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

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

static int reload ( void   )  [static]

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

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

static int unload_module ( void   )  [static]

Definition at line 665 of file res_odbc.c.

00666 {
00667    /* Prohibit unloading */
00668    return -1;
00669 }


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


Generated on Wed Aug 15 01:25:30 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3