Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:54 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen