Wed Aug 15 01:24:24 2007

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

Generated on Wed Aug 15 01:24:24 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3