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

Generated on Mon May 14 04:42:57 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1