Thu Jul 9 13:41:30 2009

Asterisk developer's documentation


res_odbc.c File Reference

ODBC resource manager. More...

#include "asterisk.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"
#include "asterisk/time.h"

Go to the source code of this file.

Data Structures

struct  odbc_class
struct  odbc_list

Functions

static void __reg_module (void)
static void __unreg_module (void)
int ast_odbc_backslash_is_escape (struct odbc_obj *obj)
 Checks if the database natively supports backslash as an escape character.
SQLHSTMT ast_odbc_direct_execute (struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
 Executes an non prepared statement and returns the resulting statement handle.
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 char * handle_cli_odbc_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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 reload (void)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, .reload = reload, }
static const struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_odbc []


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

static void __reg_module ( void   )  [static]

Definition at line 817 of file res_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 817 of file res_odbc.c.

int ast_odbc_backslash_is_escape ( struct odbc_obj obj  ) 

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

Parameters:
obj The ODBC object
Returns:
Returns 1 if backslash is a native escape character, 0 if an ESCAPE clause is needed to support '\'

Definition at line 445 of file res_odbc.c.

References odbc_class::backslash_is_escape, and odbc_obj::parent.

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

00446 {
00447    return obj->parent->backslash_is_escape;
00448 }

SQLHSTMT ast_odbc_direct_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  exec_cb,
void *  data 
)

Executes an non prepared statement and returns the resulting statement handle.

Parameters:
obj The ODBC object
exec_cb A function callback, which, when called, should return a statement handle with result columns bound.
data A parameter to be passed to the exec_cb parameter function, indicating which statement handle is to be prepared.
Return values:
a statement handle
NULL on error

Definition at line 76 of file res_odbc.c.

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

Referenced by acf_odbc_read(), and acf_odbc_write().

00077 {
00078    int attempt;
00079    SQLHSTMT stmt;
00080 
00081    for (attempt = 0; attempt < 2; attempt++) {
00082       stmt = exec_cb(obj, data);
00083 
00084       if (stmt) {
00085          break;
00086       } else {
00087          obj->up = 0;
00088          ast_log(LOG_WARNING, "SQL Exec Direct failed.  Attempting a reconnect...\n");
00089 
00090          odbc_obj_disconnect(obj);
00091          odbc_obj_connect(obj);
00092       }
00093    }
00094 
00095    return stmt;
00096 }

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.
Return values:
a statement handle
NULL on error

Definition at line 98 of file res_odbc.c.

References ast_log(), ast_odbc_sanity_check(), ast_tvnow(), LOG_WARNING, and prepare_cb().

Referenced by config_odbc(), destroy_odbc(), odbc_log(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), and update_odbc().

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

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

References odbc_obj::used.

Referenced by config_odbc(), destroy_odbc(), load_config(), odbc_log(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), and update_odbc().

00439 {
00440    /* For pooled connections, this frees the connection to be
00441     * reused.  For non-pooled connections, it does nothing. */
00442    obj->used = 0;
00443 }

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.
Return values:
ODBC object
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 450 of file res_odbc.c.

References ast_calloc, ast_free, 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_mutex_lock(), ast_mutex_unlock(), ast_odbc_sanity_check(), ast_tvdiff_sec(), ast_tvnow(), odbc_class::idlecheck, odbc_obj::last_used, odbc_class::list, odbc_obj::lock, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), odbc_obj::parent, and odbc_obj::used.

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), destroy_odbc(), load_config(), odbc_log(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), store_odbc(), and update_odbc().

00451 {
00452    struct odbc_obj *obj = NULL;
00453    struct odbc_class *class;
00454 
00455    AST_LIST_LOCK(&odbc_list);
00456    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00457       if (!strcmp(class->name, name))
00458          break;
00459    }
00460    AST_LIST_UNLOCK(&odbc_list);
00461 
00462    if (!class)
00463       return NULL;
00464 
00465    AST_LIST_LOCK(&class->odbc_obj);
00466    if (class->haspool) {
00467       /* Recycle connections before building another */
00468       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00469          if (! obj->used) {
00470             ast_mutex_lock(&obj->lock);
00471             obj->used = 1;
00472             ast_mutex_unlock(&obj->lock);
00473             break;
00474          }
00475       }
00476 
00477       if (!obj && (class->count < class->limit)) {
00478          class->count++;
00479          obj = ast_calloc(1, sizeof(*obj));
00480          if (!obj) {
00481             AST_LIST_UNLOCK(&class->odbc_obj);
00482             return NULL;
00483          }
00484          ast_mutex_init(&obj->lock);
00485          obj->parent = class;
00486          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00487             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00488             ast_mutex_destroy(&obj->lock);
00489             ast_free(obj);
00490             obj = NULL;
00491             class->count--;
00492          } else {
00493             obj->used = 1;
00494             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00495          }
00496       }
00497    } else {
00498       /* Non-pooled connection: multiple modules can use the same connection. */
00499       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00500          /* Non-pooled connection: if there is an entry, return it */
00501          break;
00502       }
00503 
00504       if (!obj) {
00505          /* No entry: build one */
00506          obj = ast_calloc(1, sizeof(*obj));
00507          if (!obj) {
00508             AST_LIST_UNLOCK(&class->odbc_obj);
00509             return NULL;
00510          }
00511          ast_mutex_init(&obj->lock);
00512          obj->parent = class;
00513          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00514             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00515             ast_mutex_destroy(&obj->lock);
00516             ast_free(obj);
00517             obj = NULL;
00518          } else {
00519             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00520          }
00521       }
00522    }
00523    AST_LIST_UNLOCK(&class->odbc_obj);
00524 
00525    if (obj && check) {
00526       ast_odbc_sanity_check(obj);
00527    } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
00528       odbc_obj_connect(obj);
00529 
00530    return obj;
00531 }

int ast_odbc_sanity_check ( struct odbc_obj obj  ) 

Checks an ODBC object to ensure it is still connected.

Parameters:
obj The ODBC object
Return values:
0 if connected
-1 otherwise.

Definition at line 193 of file res_odbc.c.

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

Referenced by ast_odbc_prepare_and_execute(), ast_odbc_request_obj(), and handle_cli_odbc_show().

00194 {
00195    char *test_sql = "select 1";
00196    SQLHSTMT stmt;
00197    int res = 0;
00198 
00199    if (!ast_strlen_zero(obj->parent->sanitysql))
00200       test_sql = obj->parent->sanitysql;
00201 
00202    if (obj->up) {
00203       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00204       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00205          obj->up = 0;
00206       } else {
00207          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00208          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00209             obj->up = 0;
00210          } else {
00211             res = SQLExecute(stmt);
00212             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00213                obj->up = 0;
00214             }
00215          }
00216       }
00217       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00218    }
00219 
00220    if (!obj->up) { /* Try to reconnect! */
00221       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00222       odbc_obj_disconnect(obj);
00223       odbc_obj_connect(obj);
00224    }
00225    return obj->up;
00226 }

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
Return values:
0 on success
-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 150 of file res_odbc.c.

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

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

static char* handle_cli_odbc_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 348 of file res_odbc.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_odbc_sanity_check(), ast_strdup, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, odbc_obj::lock, ast_cli_args::n, ast_cli_args::pos, odbc_obj::up, ast_cli_entry::usage, odbc_obj::used, and ast_cli_args::word.

00349 {
00350    struct odbc_class *class;
00351    struct odbc_obj *current;
00352    int length = 0;
00353    int which = 0;
00354    char *ret = NULL;
00355 
00356    switch (cmd) {
00357    case CLI_INIT:
00358       e->command = "odbc show";
00359       e->usage =
00360             "Usage: odbc show [class]\n"
00361             "       List settings of a particular ODBC class or,\n"
00362             "       if not specified, all classes.\n";
00363       return NULL;
00364    case CLI_GENERATE:
00365       if (a->pos != 2)
00366          return NULL;
00367       length = strlen(a->word);
00368       AST_LIST_LOCK(&odbc_list);
00369       AST_LIST_TRAVERSE(&odbc_list, class, list) {
00370          if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00371             ret = ast_strdup(class->name);
00372             break;
00373          }
00374       }
00375       if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00376          ret = ast_strdup("all");
00377       }
00378       AST_LIST_UNLOCK(&odbc_list);
00379       return ret;
00380    }
00381 
00382    ast_cli(a->fd, "\nODBC DSN Settings\n");
00383    ast_cli(a->fd, "-----------------\n\n");
00384    AST_LIST_LOCK(&odbc_list);
00385    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00386       if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00387          int count = 0;
00388          ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn);
00389 
00390          if (class->haspool) {
00391             ast_cli(a->fd, "  Pooled: Yes\n  Limit:  %d\n  Connections in use: %d\n", class->limit, class->count);
00392 
00393             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00394                ast_mutex_lock(&current->lock);
00395                ast_cli(a->fd, "    - Connection %d: %s\n", ++count, current->used ? "in use" : current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00396                ast_mutex_unlock(&current->lock);
00397             }
00398          } else {
00399             /* Should only ever be one of these */
00400             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00401                ast_cli(a->fd, "  Pooled: No\n  Connected: %s\n", current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
00402             }
00403          }
00404          ast_cli(a->fd, "\n");
00405       }
00406    }
00407    AST_LIST_UNLOCK(&odbc_list);
00408 
00409    return CLI_SUCCESS;
00410 }

static int load_module ( void   )  [static]

Definition at line 804 of file res_odbc.c.

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

00805 {
00806    if (load_odbc_config() == -1)
00807       return AST_MODULE_LOAD_DECLINE;
00808    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00809    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00810    return 0;
00811 }

static int load_odbc_config ( void   )  [static]

Definition at line 228 of file res_odbc.c.

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

Referenced by load_module().

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

static odbc_status odbc_obj_connect ( struct odbc_obj obj  )  [static]

Definition at line 562 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_tvnow(), odbc_obj::con, odbc_class::dsn, odbc_class::env, odbc_obj::last_used, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, msg, odbc_class::name, ODBC_FAIL, odbc_obj_disconnect(), ODBC_SUCCESS, odbc_obj::parent, odbc_class::password, odbc_obj::up, and odbc_class::username.

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

00563 {
00564    int res;
00565    SQLINTEGER err;
00566    short int mlen;
00567    unsigned char msg[200], stat[10];
00568 #ifdef NEEDTRACE
00569    SQLINTEGER enable = 1;
00570    char *tracefile = "/tmp/odbc.trace";
00571 #endif
00572    ast_mutex_lock(&obj->lock);
00573 
00574    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00575 
00576    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00577       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00578       ast_mutex_unlock(&obj->lock);
00579       return ODBC_FAIL;
00580    }
00581    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00582    SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00583 #ifdef NEEDTRACE
00584    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00585    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00586 #endif
00587 
00588    if (obj->up) {
00589       odbc_obj_disconnect(obj);
00590       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00591    } else {
00592       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00593    }
00594 
00595    res = SQLConnect(obj->con,
00596          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00597          (SQLCHAR *) obj->parent->username, SQL_NTS,
00598          (SQLCHAR *) obj->parent->password, SQL_NTS);
00599 
00600    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00601       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00602       ast_mutex_unlock(&obj->lock);
00603       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00604       return ODBC_FAIL;
00605    } else {
00606       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00607       obj->up = 1;
00608       obj->last_used = ast_tvnow();
00609    }
00610 
00611    ast_mutex_unlock(&obj->lock);
00612    return ODBC_SUCCESS;
00613 }

static odbc_status odbc_obj_disconnect ( struct odbc_obj obj  )  [static]

Definition at line 533 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_class::dsn, odbc_obj::lock, LOG_DEBUG, LOG_WARNING, msg, odbc_class::name, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.

Referenced by ast_odbc_direct_execute(), ast_odbc_sanity_check(), ast_odbc_smart_execute(), and odbc_obj_connect().

00534 {
00535    int res;
00536    SQLINTEGER err;
00537    short int mlen;
00538    unsigned char msg[200], stat[10];
00539 
00540    ast_mutex_lock(&obj->lock);
00541 
00542    res = SQLDisconnect(obj->con);
00543 
00544    if (res == ODBC_SUCCESS) {
00545       ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00546    } else {
00547       ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00548    }
00549 
00550    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == ODBC_SUCCESS)) {
00551       ast_log(LOG_DEBUG, "Database handle deallocated\n");
00552    } else {
00553       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00554       ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00555    }
00556 
00557    obj->up = 0;
00558    ast_mutex_unlock(&obj->lock);
00559    return ODBC_SUCCESS;
00560 }

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

Definition at line 416 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(), LOG_WARNING, and odbc_class::name.

00417 {
00418    struct odbc_obj *obj;
00419    if (class) {
00420       AST_LIST_LOCK(&odbc_list);
00421       AST_LIST_INSERT_HEAD(&odbc_list, class, list);
00422       AST_LIST_UNLOCK(&odbc_list);
00423 
00424       if (connect) {
00425          /* Request and release builds a connection */
00426          obj = ast_odbc_request_obj(class->name, 0);
00427          if (obj)
00428             ast_odbc_release_obj(obj);
00429       }
00430 
00431       return 0;
00432    } else {
00433       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00434       return -1;
00435    }
00436 }

static int reload ( void   )  [static]

Definition at line 615 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, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEUNCHANGED, enabled, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, setenv(), and ast_variable::value.

00616 {
00617    static char *cfg = "res_odbc.conf";
00618    struct ast_config *config;
00619    struct ast_variable *v;
00620    char *cat;
00621    const char *dsn, *username, *password, *sanitysql;
00622    int enabled, pooling, limit, bse;
00623    unsigned int idlecheck;
00624    int connect = 0, res = 0;
00625    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00626 
00627    struct odbc_class *new, *class;
00628    struct odbc_obj *current;
00629 
00630    /* First, mark all to be purged */
00631    AST_LIST_LOCK(&odbc_list);
00632    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00633       class->delme = 1;
00634    }
00635 
00636    config = ast_config_load(cfg, config_flags);
00637    if (config != NULL && config != CONFIG_STATUS_FILEUNCHANGED) {
00638       for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00639          if (!strcasecmp(cat, "ENV")) {
00640             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00641                setenv(v->name, v->value, 1);
00642                ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00643             }
00644          } else {
00645             char *freeme = NULL;
00646             /* Reset all to defaults for each class of odbc connections */
00647             dsn = username = password = sanitysql = NULL;
00648             enabled = 1;
00649             connect = idlecheck = 0;
00650             pooling = 0;
00651             limit = 0;
00652             bse = 1;
00653             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00654                if (!strcasecmp(v->name, "pooling")) {
00655                   if (ast_true(v->value))
00656                      pooling = 1;
00657                } else if (!strncasecmp(v->name, "share", 5)) {
00658                   /* "shareconnections" is a little clearer in meaning than "pooling" */
00659                   if (ast_false(v->value))
00660                      pooling = 1;
00661                } else if (!strcasecmp(v->name, "limit")) {
00662                   sscanf(v->value, "%d", &limit);
00663                   if (ast_true(v->value) && !limit) {
00664                      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);
00665                      limit = 1023;
00666                   } else if (ast_false(v->value)) {
00667                      ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00668                      enabled = 0;
00669                      break;
00670                   }
00671                } else if (!strcasecmp(v->name, "idlecheck")) {
00672                   sscanf(v->value, "%ud", &idlecheck);
00673                } else if (!strcasecmp(v->name, "enabled")) {
00674                   enabled = ast_true(v->value);
00675                } else if (!strcasecmp(v->name, "pre-connect")) {
00676                   connect = ast_true(v->value);
00677                } else if (!strcasecmp(v->name, "dsn")) {
00678                   dsn = v->value;
00679                } else if (!strcasecmp(v->name, "username")) {
00680                   username = v->value;
00681                } else if (!strcasecmp(v->name, "password")) {
00682                   password = v->value;
00683                } else if (!strcasecmp(v->name, "sanitysql")) {
00684                   sanitysql = v->value;
00685                } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00686                   bse = ast_true(v->value);
00687                }
00688             }
00689 
00690             if (enabled && !ast_strlen_zero(dsn)) {
00691                /* First, check the list to see if it already exists */
00692                AST_LIST_TRAVERSE(&odbc_list, class, list) {
00693                   if (!strcmp(class->name, cat)) {
00694                      class->delme = 0;
00695                      break;
00696                   }
00697                }
00698 
00699                if (class) {
00700                   new = class;
00701                } else {
00702                   new = ast_calloc(1, sizeof(*new));
00703                }
00704 
00705                if (!new) {
00706                   res = -1;
00707                   break;
00708                }
00709 
00710                if (cat)
00711                   ast_copy_string(new->name, cat, sizeof(new->name));
00712                if (dsn)
00713                   ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00714 
00715                /* Safely replace username */
00716                if (class && class->username)
00717                   freeme = class->username;
00718                if (username)
00719                   new->username = ast_strdup(username);
00720                if (freeme) {
00721                   ast_free(freeme);
00722                   freeme = NULL;
00723                }
00724 
00725                /* Safely replace password */
00726                if (class && class->password)
00727                    freeme = class->password;
00728                if (password)
00729                   new->password = ast_strdup(password);
00730                if (freeme) {
00731                   ast_free(freeme);
00732                   freeme = NULL;
00733                }
00734 
00735                if (sanitysql)
00736                   ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql));
00737 
00738                if (!class) {
00739                   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00740                   res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00741 
00742                   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00743                      ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00744                      SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00745                      AST_LIST_UNLOCK(&odbc_list);
00746                      return res;
00747                   }
00748                }
00749 
00750                if (pooling) {
00751                   new->haspool = pooling;
00752                   if (limit) {
00753                      new->limit = limit;
00754                   } else {
00755                      ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00756                      new->limit = 5;
00757                   }
00758                }
00759 
00760                new->backslash_is_escape = bse;
00761                new->idlecheck = idlecheck;
00762 
00763                if (class) {
00764                   ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
00765                } else {
00766                   odbc_register_class(new, connect);
00767                   ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00768                }
00769             }
00770          }
00771       }
00772       ast_config_destroy(config);
00773    }
00774 
00775    /* Purge classes that we know can go away (pooled with 0, only) */
00776    AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
00777       if (class->delme && class->haspool && class->count == 0) {
00778          while ((current = AST_LIST_REMOVE_HEAD(&class->odbc_obj, list))) {
00779             odbc_obj_disconnect(current);
00780             ast_mutex_destroy(&current->lock);
00781             ast_free(current);
00782          }
00783 
00784          AST_LIST_REMOVE_CURRENT(list);
00785          if (class->username)
00786             ast_free(class->username);
00787          if (class->password)
00788             ast_free(class->password);
00789          ast_free(class);
00790       }
00791    }
00792    AST_LIST_TRAVERSE_SAFE_END;
00793    AST_LIST_UNLOCK(&odbc_list);
00794 
00795    return 0;
00796 }

static int unload_module ( void   )  [static]

Definition at line 798 of file res_odbc.c.

00799 {
00800    /* Prohibit unloading */
00801    return -1;
00802 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "ODBC resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 817 of file res_odbc.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 817 of file res_odbc.c.

struct ast_cli_entry cli_odbc[] [static]

Initial value:

 {
   { .handler =  handle_cli_odbc_show , .summary =  "List ODBC DSN(s)" ,__VA_ARGS__ }
}

Definition at line 412 of file res_odbc.c.

Referenced by load_module().


Generated on Thu Jul 9 13:41:30 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7