Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:01:46 2007

Asterisk developer's documentation :: Codename Pineapple


res_odbc.c File Reference


Detailed Description

ODBC resource manager.

Author:
Mark Spencer <markster@digium.com>

Anthony Minessale II <anthmct@yahoo.com>

Definition in file res_odbc.c.

#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().
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 const char show_usage []


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(), and LOG_WARNING.

Referenced by config_odbc().

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 + 1; 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             ast_odbc_sanity_check(obj);
00120             continue;
00121          }
00122          break;
00123       } else if (attempt == 0)
00124          ast_odbc_sanity_check(obj);
00125    }
00126 
00127    return stmt;
00128 }

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

References odbc_obj::used.

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

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

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 384 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, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), and odbc_obj::used.

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

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

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

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

Referenced by ast_odbc_request_obj(), and odbc_show_command().

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

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

Referenced by realtime_multi_odbc(), realtime_odbc(), and update_odbc().

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

static int load_module void   )  [static]
 

Definition at line 680 of file res_odbc.c.

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

00681 {
00682    if(load_odbc_config() == -1)
00683       return AST_MODULE_LOAD_DECLINE;
00684    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00685    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00686    return 0;
00687 }

static int load_odbc_config void   )  [static]
 

Definition at line 207 of file res_odbc.c.

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

Referenced by load_module().

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

static odbc_status odbc_obj_connect struct odbc_obj obj  )  [static]
 

Definition at line 473 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_request_obj(), ast_odbc_sanity_check(), and ast_odbc_smart_execute().

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

static odbc_status odbc_obj_disconnect struct odbc_obj obj  )  [static]
 

Definition at line 455 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_sanity_check(), ast_odbc_smart_execute(), and odbc_obj_connect().

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

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

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

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

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

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

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

static int reload void   )  [static]
 

Definition at line 527 of file res_odbc.c.

References ast_category_browse(), ast_config_load(), 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.

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

static int unload_module void   )  [static]
 

Definition at line 674 of file res_odbc.c.

00675 {
00676    /* Prohibit unloading */
00677    return -1;
00678 }


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

Referenced by load_module().

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


Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen