Sat Apr 12 07:12:27 2008

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: 89559 $")
00040 
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 #include <unistd.h>
00044 #include <string.h>
00045 
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/pbx.h"
00052 #include "asterisk/module.h"
00053 #include "asterisk/cli.h"
00054 #include "asterisk/lock.h"
00055 #include "asterisk/res_odbc.h"
00056 
00057 struct odbc_class
00058 {
00059    AST_LIST_ENTRY(odbc_class) list;
00060    char name[80];
00061    char dsn[80];
00062    char username[80];
00063    char password[80];
00064    SQLHENV env;
00065    unsigned int haspool:1;         /* Boolean - TDS databases need this */
00066    unsigned int limit:10;          /* Gives a limit of 1023 maximum */
00067    unsigned int count:10;          /* Running count of pooled connections */
00068    unsigned int delme:1;         /* Purge the class */
00069    unsigned int backslash_is_escape:1; /* On this database, the backslash is a native escape sequence */
00070    AST_LIST_HEAD(, odbc_obj) odbc_obj;
00071 };
00072 
00073 AST_LIST_HEAD_STATIC(odbc_list, odbc_class);
00074 
00075 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
00076 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
00077 static int odbc_register_class(struct odbc_class *class, int connect);
00078 
00079 
00080 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
00081 {
00082    int res = 0, i, attempt;
00083    SQLINTEGER nativeerror=0, numfields=0;
00084    SQLSMALLINT diagbytes=0;
00085    unsigned char state[10], diagnostic[256];
00086    SQLHSTMT stmt;
00087 
00088    for (attempt = 0; attempt < 2; attempt++) {
00089       /* This prepare callback may do more than just prepare -- it may also
00090        * bind parameters, bind results, etc.  The real key, here, is that
00091        * when we disconnect, all handles become invalid for most databases.
00092        * We must therefore redo everything when we establish a new
00093        * connection. */
00094       stmt = prepare_cb(obj, data);
00095 
00096       if (stmt) {
00097          res = SQLExecute(stmt);
00098          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00099             if (res == SQL_ERROR) {
00100                SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00101                for (i = 0; i < numfields; i++) {
00102                   SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00103                   ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00104                   if (i > 10) {
00105                      ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00106                      break;
00107                   }
00108                }
00109             }
00110 
00111             ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00112             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00113             stmt = NULL;
00114 
00115             obj->up = 0;
00116             /*
00117              * While this isn't the best way to try to correct an error, this won't automatically
00118              * fail when the statement handle invalidates.
00119              */
00120             /* XXX Actually, it might, if we're using a non-pooled connection. Possible race here. XXX */
00121             odbc_obj_disconnect(obj);
00122             odbc_obj_connect(obj);
00123             continue;
00124          }
00125          break;
00126       } else {
00127          ast_log(LOG_WARNING, "SQL Prepare failed.  Attempting a reconnect...\n");
00128          odbc_obj_disconnect(obj);
00129          odbc_obj_connect(obj);
00130       }
00131    }
00132 
00133    return stmt;
00134 }
00135 
00136 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) 
00137 {
00138    int res = 0, i;
00139    SQLINTEGER nativeerror=0, numfields=0;
00140    SQLSMALLINT diagbytes=0;
00141    unsigned char state[10], diagnostic[256];
00142 
00143    res = SQLExecute(stmt);
00144    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00145       if (res == SQL_ERROR) {
00146          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00147          for (i = 0; i < numfields; i++) {
00148             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00149             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00150             if (i > 10) {
00151                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00152                break;
00153             }
00154          }
00155       }
00156 #if 0
00157       /* This is a really bad method of trying to correct a dead connection.  It
00158        * only ever really worked with MySQL.  It will not work with any other
00159        * database, since most databases prepare their statements on the server,
00160        * and if you disconnect, you invalidate the statement handle.  Hence, if
00161        * you disconnect, you're going to fail anyway, whether you try to execute
00162        * a second time or not.
00163        */
00164       ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00165       ast_mutex_lock(&obj->lock);
00166       obj->up = 0;
00167       ast_mutex_unlock(&obj->lock);
00168       odbc_obj_disconnect(obj);
00169       odbc_obj_connect(obj);
00170       res = SQLExecute(stmt);
00171 #endif
00172    }
00173    
00174    return res;
00175 }
00176 
00177 
00178 int ast_odbc_sanity_check(struct odbc_obj *obj) 
00179 {
00180    char *test_sql = "select 1";
00181    SQLHSTMT stmt;
00182    int res = 0;
00183 
00184    if (obj->up) {
00185       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00186       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00187          obj->up = 0;
00188       } else {
00189          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00190          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00191             obj->up = 0;
00192          } else {
00193             res = SQLExecute(stmt);
00194             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00195                obj->up = 0;
00196             }
00197          }
00198       }
00199       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00200    }
00201 
00202    if (!obj->up) { /* Try to reconnect! */
00203       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00204       odbc_obj_disconnect(obj);
00205       odbc_obj_connect(obj);
00206    }
00207    return obj->up;
00208 }
00209 
00210 static int load_odbc_config(void)
00211 {
00212    static char *cfg = "res_odbc.conf";
00213    struct ast_config *config;
00214    struct ast_variable *v;
00215    char *cat, *dsn, *username, *password;
00216    int enabled, pooling, limit, bse;
00217    int connect = 0, res = 0;
00218 
00219    struct odbc_class *new;
00220 
00221    config = ast_config_load(cfg);
00222    if (!config) {
00223       ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00224       return -1;
00225    }
00226    for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00227       if (!strcasecmp(cat, "ENV")) {
00228          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00229             setenv(v->name, v->value, 1);
00230             ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00231          }
00232       } else {
00233          /* Reset all to defaults for each class of odbc connections */
00234          dsn = username = password = NULL;
00235          enabled = 1;
00236          connect = 0;
00237          pooling = 0;
00238          limit = 0;
00239          bse = 1;
00240          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00241             if (!strcasecmp(v->name, "pooling")) {
00242                if (ast_true(v->value))
00243                   pooling = 1;
00244             } else if (!strcasecmp(v->name, "limit")) {
00245                sscanf(v->value, "%d", &limit);
00246                if (ast_true(v->value) && !limit) {
00247                   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);
00248                   limit = 1023;
00249                } else if (ast_false(v->value)) {
00250                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00251                   enabled = 0;
00252                   break;
00253                }
00254             } else if (!strcasecmp(v->name, "enabled")) {
00255                enabled = ast_true(v->value);
00256             } else if (!strcasecmp(v->name, "pre-connect")) {
00257                connect = ast_true(v->value);
00258             } else if (!strcasecmp(v->name, "dsn")) {
00259                dsn = v->value;
00260             } else if (!strcasecmp(v->name, "username")) {
00261                username = v->value;
00262             } else if (!strcasecmp(v->name, "password")) {
00263                password = v->value;
00264             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00265                bse = ast_true(v->value);
00266             }
00267          }
00268 
00269          if (enabled && !ast_strlen_zero(dsn)) {
00270             new = ast_calloc(1, sizeof(*new));
00271 
00272             if (!new) {
00273                res = -1;
00274                break;
00275             }
00276 
00277             if (cat)
00278                ast_copy_string(new->name, cat, sizeof(new->name));
00279             if (dsn)
00280                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00281             if (username)
00282                ast_copy_string(new->username, username, sizeof(new->username));
00283             if (password)
00284                ast_copy_string(new->password, password, sizeof(new->password));
00285 
00286             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00287             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00288 
00289             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00290                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00291                SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00292                return res;
00293             }
00294 
00295             if (pooling) {
00296                new->haspool = pooling;
00297                if (limit) {
00298                   new->limit = limit;
00299                } else {
00300                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00301                   new->limit = 5;
00302                }
00303             }
00304 
00305             new->backslash_is_escape = bse ? 1 : 0;
00306 
00307             odbc_register_class(new, connect);
00308             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00309          }
00310       }
00311    }
00312    ast_config_destroy(config);
00313    return res;
00314 }
00315 
00316 static int odbc_show_command(int fd, int argc, char **argv)
00317 {
00318    struct odbc_class *class;
00319    struct odbc_obj *current;
00320 
00321    AST_LIST_LOCK(&odbc_list);
00322    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00323       if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) {
00324          int count = 0;
00325          ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn);
00326 
00327          if (class->haspool) {
00328             ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count);
00329 
00330             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00331                ast_cli(fd, "  Connection %d: %s\n", ++count, current->used ? "in use" : current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00332             }
00333          } else {
00334             /* Should only ever be one of these */
00335             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00336                ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no");
00337             }
00338          }
00339 
00340             ast_cli(fd, "\n");
00341       }
00342    }
00343    AST_LIST_UNLOCK(&odbc_list);
00344 
00345    return 0;
00346 }
00347 
00348 static char show_usage[] =
00349 "Usage: odbc show [<class>]\n"
00350 "       List settings of a particular ODBC class.\n"
00351 "       or, if not specified, all classes.\n";
00352 
00353 static struct ast_cli_entry cli_odbc[] = {
00354    { { "odbc", "show", NULL },
00355    odbc_show_command, "List ODBC DSN(s)",
00356    show_usage },
00357 };
00358 
00359 static int odbc_register_class(struct odbc_class *class, int connect)
00360 {
00361    struct odbc_obj *obj;
00362    if (class) {
00363       AST_LIST_LOCK(&odbc_list);
00364       AST_LIST_INSERT_HEAD(&odbc_list, class, list);
00365       AST_LIST_UNLOCK(&odbc_list);
00366 
00367       if (connect) {
00368          /* Request and release builds a connection */
00369          obj = ast_odbc_request_obj(class->name, 0);
00370          if (obj)
00371             ast_odbc_release_obj(obj);
00372       }
00373 
00374       return 0;
00375    } else {
00376       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00377       return -1;
00378    }
00379 }
00380 
00381 void ast_odbc_release_obj(struct odbc_obj *obj)
00382 {
00383    /* For pooled connections, this frees the connection to be
00384     * reused.  For non-pooled connections, it does nothing. */
00385    obj->used = 0;
00386 }
00387 
00388 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
00389 {
00390    return obj->parent->backslash_is_escape;
00391 }
00392 
00393 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
00394 {
00395    struct odbc_obj *obj = NULL;
00396    struct odbc_class *class;
00397 
00398    AST_LIST_LOCK(&odbc_list);
00399    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00400       if (!strcmp(class->name, name))
00401          break;
00402    }
00403    AST_LIST_UNLOCK(&odbc_list);
00404 
00405    if (!class)
00406       return NULL;
00407 
00408    AST_LIST_LOCK(&class->odbc_obj);
00409    if (class->haspool) {
00410       /* Recycle connections before building another */
00411       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00412          if (! obj->used) {
00413             obj->used = 1;
00414             break;
00415          }
00416       }
00417 
00418       if (!obj && (class->count < class->limit)) {
00419          class->count++;
00420          obj = ast_calloc(1, sizeof(*obj));
00421          if (!obj) {
00422             AST_LIST_UNLOCK(&class->odbc_obj);
00423             return NULL;
00424          }
00425          ast_mutex_init(&obj->lock);
00426          obj->parent = class;
00427          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00428             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00429             ast_mutex_destroy(&obj->lock);
00430             free(obj);
00431             obj = NULL;
00432             class->count--;
00433          } else {
00434             obj->used = 1;
00435             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00436          }
00437       }
00438    } else {
00439       /* Non-pooled connection: multiple modules can use the same connection. */
00440       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00441          /* Non-pooled connection: if there is an entry, return it */
00442          break;
00443       }
00444 
00445       if (!obj) {
00446          /* No entry: build one */
00447          obj = ast_calloc(1, sizeof(*obj));
00448          if (!obj) {
00449             AST_LIST_UNLOCK(&class->odbc_obj);
00450             return NULL;
00451          }
00452          ast_mutex_init(&obj->lock);
00453          obj->parent = class;
00454          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00455             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00456             ast_mutex_destroy(&obj->lock);
00457             free(obj);
00458             obj = NULL;
00459          } else {
00460             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00461          }
00462       }
00463    }
00464    AST_LIST_UNLOCK(&class->odbc_obj);
00465 
00466    if (obj && check) {
00467       ast_odbc_sanity_check(obj);
00468    }
00469    return obj;
00470 }
00471 
00472 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
00473 {
00474    int res;
00475    ast_mutex_lock(&obj->lock);
00476 
00477    res = SQLDisconnect(obj->con);
00478 
00479    if (res == ODBC_SUCCESS) {
00480       ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00481    } else {
00482       ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n",
00483       obj->parent->name, obj->parent->dsn);
00484    }
00485    obj->up = 0;
00486    ast_mutex_unlock(&obj->lock);
00487    return ODBC_SUCCESS;
00488 }
00489 
00490 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
00491 {
00492    int res;
00493    SQLINTEGER err;
00494    short int mlen;
00495    unsigned char msg[200], stat[10];
00496 #ifdef NEEDTRACE
00497    SQLINTEGER enable = 1;
00498    char *tracefile = "/tmp/odbc.trace";
00499 #endif
00500    ast_mutex_lock(&obj->lock);
00501 
00502    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00503 
00504    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00505       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00506       ast_mutex_unlock(&obj->lock);
00507       return ODBC_FAIL;
00508    }
00509    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00510 #ifdef NEEDTRACE
00511    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00512    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00513 #endif
00514 
00515    if (obj->up) {
00516       odbc_obj_disconnect(obj);
00517       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00518    } else {
00519       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00520    }
00521 
00522    res = SQLConnect(obj->con,
00523          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00524          (SQLCHAR *) obj->parent->username, SQL_NTS,
00525          (SQLCHAR *) obj->parent->password, SQL_NTS);
00526 
00527    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00528       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00529       ast_mutex_unlock(&obj->lock);
00530       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00531       return ODBC_FAIL;
00532    } else {
00533       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00534       obj->up = 1;
00535    }
00536 
00537    ast_mutex_unlock(&obj->lock);
00538    return ODBC_SUCCESS;
00539 }
00540 
00541 static int reload(void)
00542 {
00543    static char *cfg = "res_odbc.conf";
00544    struct ast_config *config;
00545    struct ast_variable *v;
00546    char *cat, *dsn, *username, *password;
00547    int enabled, pooling, limit, bse;
00548    int connect = 0, res = 0;
00549 
00550    struct odbc_class *new, *class;
00551    struct odbc_obj *current;
00552 
00553    /* First, mark all to be purged */
00554    AST_LIST_LOCK(&odbc_list);
00555    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00556       class->delme = 1;
00557    }
00558 
00559    config = ast_config_load(cfg);
00560    if (config) {
00561       for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00562          if (!strcasecmp(cat, "ENV")) {
00563             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00564                setenv(v->name, v->value, 1);
00565                ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00566             }
00567          } else {
00568             /* Reset all to defaults for each class of odbc connections */
00569             dsn = username = password = NULL;
00570             enabled = 1;
00571             connect = 0;
00572             pooling = 0;
00573             limit = 0;
00574             bse = 1;
00575             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00576                if (!strcasecmp(v->name, "pooling")) {
00577                   pooling = 1;
00578                } else if (!strcasecmp(v->name, "limit")) {
00579                   sscanf(v->value, "%d", &limit);
00580                   if (ast_true(v->value) && !limit) {
00581                      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);
00582                      limit = 1023;
00583                   } else if (ast_false(v->value)) {
00584                      ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00585                      enabled = 0;
00586                      break;
00587                   }
00588                } else if (!strcasecmp(v->name, "enabled")) {
00589                   enabled = ast_true(v->value);
00590                } else if (!strcasecmp(v->name, "pre-connect")) {
00591                   connect = ast_true(v->value);
00592                } else if (!strcasecmp(v->name, "dsn")) {
00593                   dsn = v->value;
00594                } else if (!strcasecmp(v->name, "username")) {
00595                   username = v->value;
00596                } else if (!strcasecmp(v->name, "password")) {
00597                   password = v->value;
00598                } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00599                   bse = ast_true(v->value);
00600                }
00601             }
00602 
00603             if (enabled && !ast_strlen_zero(dsn)) {
00604                /* First, check the list to see if it already exists */
00605                AST_LIST_TRAVERSE(&odbc_list, class, list) {
00606                   if (!strcmp(class->name, cat)) {
00607                      class->delme = 0;
00608                      break;
00609                   }
00610                }
00611 
00612                if (class) {
00613                   new = class;
00614                } else {
00615                   new = ast_calloc(1, sizeof(*new));
00616                }
00617 
00618                if (!new) {
00619                   res = -1;
00620                   break;
00621                }
00622 
00623                if (cat)
00624                   ast_copy_string(new->name, cat, sizeof(new->name));
00625                if (dsn)
00626                   ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00627                if (username)
00628                   ast_copy_string(new->username, username, sizeof(new->username));
00629                if (password)
00630                   ast_copy_string(new->password, password, sizeof(new->password));
00631 
00632                if (!class) {
00633                   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00634                   res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00635 
00636                   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00637                      ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00638                      SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00639                      AST_LIST_UNLOCK(&odbc_list);
00640                      return res;
00641                   }
00642                }
00643 
00644                if (pooling) {
00645                   new->haspool = pooling;
00646                   if (limit) {
00647                      new->limit = limit;
00648                   } else {
00649                      ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00650                      new->limit = 5;
00651                   }
00652                }
00653 
00654                new->backslash_is_escape = bse;
00655 
00656                if (class) {
00657                   ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
00658                } else {
00659                   odbc_register_class(new, connect);
00660                   ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00661                }
00662             }
00663          }
00664       }
00665       ast_config_destroy(config);
00666    }
00667 
00668    /* Purge classes that we know can go away (pooled with 0, only) */
00669    AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
00670       if (class->delme && class->haspool && class->count == 0) {
00671          AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) {
00672             AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list);
00673             odbc_obj_disconnect(current);
00674             ast_mutex_destroy(&current->lock);
00675             free(current);
00676          }
00677          AST_LIST_TRAVERSE_SAFE_END;
00678 
00679          AST_LIST_REMOVE_CURRENT(&odbc_list, list);
00680          free(class);
00681       }
00682    }
00683    AST_LIST_TRAVERSE_SAFE_END;
00684    AST_LIST_UNLOCK(&odbc_list);
00685 
00686    return 0;
00687 }
00688 
00689 static int unload_module(void)
00690 {
00691    /* Prohibit unloading */
00692    return -1;
00693 }
00694 
00695 static int load_module(void)
00696 {
00697    if(load_odbc_config() == -1)
00698       return AST_MODULE_LOAD_DECLINE;
00699    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00700    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00701    return 0;
00702 }
00703 
00704 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Resource",
00705       .load = load_module,
00706       .unload = unload_module,
00707       .reload = reload,
00708           );

Generated on Sat Apr 12 07:12:27 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5