Wed Oct 28 11:45:41 2009

Asterisk developer's documentation


res_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * res_odbc.c <ODBC resource manager>
00009  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief ODBC resource manager
00025  * 
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author Anthony Minessale II <anthmct@yahoo.com>
00028  *
00029  * \arg See also: \ref cdr_odbc
00030  */
00031 
00032 /*** MODULEINFO
00033    <depend>unixodbc</depend>
00034    <depend>ltdl</depend>
00035  ***/
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211551 $")
00040 
00041 #include "asterisk/file.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/lock.h"
00048 #include "asterisk/res_odbc.h"
00049 #include "asterisk/time.h"
00050 
00051 struct odbc_class
00052 {
00053    AST_LIST_ENTRY(odbc_class) list;
00054    char name[80];
00055    char dsn[80];
00056    char *username;
00057    char *password;
00058    char sanitysql[256];
00059    SQLHENV env;
00060    unsigned int haspool:1;         /* Boolean - TDS databases need this */
00061    unsigned int limit:10;          /* Gives a limit of 1023 maximum */
00062    unsigned int count:10;          /* Running count of pooled connections */
00063    unsigned int delme:1;         /* Purge the class */
00064    unsigned int backslash_is_escape:1; /* On this database, the backslash is a native escape sequence */
00065    unsigned int idlecheck;       /* Recheck the connection if it is idle for this long */
00066    AST_LIST_HEAD(, odbc_obj) odbc_obj;
00067 };
00068 
00069 AST_LIST_HEAD_STATIC(odbc_list, odbc_class);
00070 
00071 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
00072 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
00073 static int odbc_register_class(struct odbc_class *class, int connect);
00074 
00075 
00076 SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
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 }
00097 
00098 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
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 }
00149 
00150 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) 
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 }
00191 
00192 
00193 int ast_odbc_sanity_check(struct odbc_obj *obj) 
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 }
00227 
00228 static int load_odbc_config(void)
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, "%30d", &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, "%30u", &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 }
00347 
00348 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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 }
00411 
00412 static struct ast_cli_entry cli_odbc[] = {
00413    AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
00414 };
00415 
00416 static int odbc_register_class(struct odbc_class *class, int connect)
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 }
00437 
00438 void ast_odbc_release_obj(struct odbc_obj *obj)
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 }
00444 
00445 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
00446 {
00447    return obj->parent->backslash_is_escape;
00448 }
00449 
00450 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
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 }
00532 
00533 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
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 }
00561 
00562 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
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 }
00614 
00615 static int reload(void)
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, "%30d", &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, "%30u", &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 }
00797 
00798 static int unload_module(void)
00799 {
00800    /* Prohibit unloading */
00801    return -1;
00802 }
00803 
00804 static int load_module(void)
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 }
00812 
00813 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
00814       .load = load_module,
00815       .unload = unload_module,
00816       .reload = reload,
00817           );

Generated on Wed Oct 28 11:45:42 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6