Sat Aug 6 00:39:32 2011

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

Generated on Sat Aug 6 00:39:32 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7