Fri Aug 24 02:27:21 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().
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(), and odbc_obj_disconnect().

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), realtime_multi_odbc(), realtime_odbc(), and update_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; 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.

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

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 
)

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

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          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00417             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00418             ast_mutex_destroy(&obj->lock);
00419             free(obj);
00420             obj = NULL;
00421             class->count--;
00422          } else {
00423             obj->used = 1;
00424             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00425          }
00426       }
00427    } else {
00428       /* Non-pooled connection: multiple modules can use the same connection. */
00429       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00430          /* Non-pooled connection: if there is an entry, return it */
00431          break;
00432       }
00433 
00434       if (!obj) {
00435          /* No entry: build one */
00436          obj = ast_calloc(1, sizeof(*obj));
00437          if (!obj) {
00438             AST_LIST_UNLOCK(&class->odbc_obj);
00439             return NULL;
00440          }
00441          ast_mutex_init(&obj->lock);
00442          obj->parent = class;
00443          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00444             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00445             ast_mutex_destroy(&obj->lock);
00446             free(obj);
00447             obj = NULL;
00448          } else {
00449             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00450          }
00451       }
00452    }
00453    AST_LIST_UNLOCK(&class->odbc_obj);
00454 
00455    if (obj && check) {
00456       ast_odbc_sanity_check(obj);
00457    }
00458    return obj;
00459 }

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.

Referenced by ast_odbc_request_obj(), and odbc_show_command().

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

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

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

static int load_odbc_config ( void   )  [static]

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

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

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

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

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

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

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.

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

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

static int unload_module ( void   )  [static]

Definition at line 673 of file res_odbc.c.

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


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.

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


Generated on Fri Aug 24 02:27:21 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1