Mon Nov 24 15:34:20 2008

Asterisk developer's documentation


res_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * res_odbc.c <ODBC resource manager>
00009  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief ODBC resource manager
00025  * 
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author Anthony Minessale II <anthmct@yahoo.com>
00028  *
00029  * \arg See also: \ref cdr_odbc
00030  */
00031 
00032 /*** MODULEINFO
00033    <depend>unixodbc</depend>
00034    <depend>ltdl</depend>
00035  ***/
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 137138 $")
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! Attempting a reconnect...\n", res);
00114             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00115             stmt = NULL;
00116 
00117             obj->up = 0;
00118             /*
00119              * While this isn't the best way to try to correct an error, this won't automatically
00120              * fail when the statement handle invalidates.
00121              */
00122             /* XXX Actually, it might, if we're using a non-pooled connection. Possible race here. XXX */
00123             odbc_obj_disconnect(obj);
00124             odbc_obj_connect(obj);
00125             continue;
00126          } else
00127             obj->last_used = ast_tvnow();
00128          break;
00129       } else {
00130          ast_log(LOG_WARNING, "SQL Prepare failed.  Attempting a reconnect...\n");
00131          odbc_obj_disconnect(obj);
00132          odbc_obj_connect(obj);
00133       }
00134    }
00135 
00136    return stmt;
00137 }
00138 
00139 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) 
00140 {
00141    int res = 0, i;
00142    SQLINTEGER nativeerror=0, numfields=0;
00143    SQLSMALLINT diagbytes=0;
00144    unsigned char state[10], diagnostic[256];
00145 
00146    res = SQLExecute(stmt);
00147    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00148       if (res == SQL_ERROR) {
00149          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00150          for (i = 0; i < numfields; i++) {
00151             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00152             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00153             if (i > 10) {
00154                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00155                break;
00156             }
00157          }
00158       }
00159 #if 0
00160       /* This is a really bad method of trying to correct a dead connection.  It
00161        * only ever really worked with MySQL.  It will not work with any other
00162        * database, since most databases prepare their statements on the server,
00163        * and if you disconnect, you invalidate the statement handle.  Hence, if
00164        * you disconnect, you're going to fail anyway, whether you try to execute
00165        * a second time or not.
00166        */
00167       ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00168       ast_mutex_lock(&obj->lock);
00169       obj->up = 0;
00170       ast_mutex_unlock(&obj->lock);
00171       odbc_obj_disconnect(obj);
00172       odbc_obj_connect(obj);
00173       res = SQLExecute(stmt);
00174 #endif
00175    } else
00176       obj->last_used = ast_tvnow();
00177    
00178    return res;
00179 }
00180 
00181 
00182 int ast_odbc_sanity_check(struct odbc_obj *obj) 
00183 {
00184    char *test_sql = "select 1";
00185    SQLHSTMT stmt;
00186    int res = 0;
00187 
00188    if (obj->up) {
00189       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00190       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00191          obj->up = 0;
00192       } else {
00193          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00194          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00195             obj->up = 0;
00196          } else {
00197             res = SQLExecute(stmt);
00198             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00199                obj->up = 0;
00200             }
00201          }
00202       }
00203       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00204    }
00205 
00206    if (!obj->up) { /* Try to reconnect! */
00207       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00208       odbc_obj_disconnect(obj);
00209       odbc_obj_connect(obj);
00210    }
00211    return obj->up;
00212 }
00213 
00214 static int load_odbc_config(void)
00215 {
00216    static char *cfg = "res_odbc.conf";
00217    struct ast_config *config;
00218    struct ast_variable *v;
00219    char *cat, *dsn, *username, *password;
00220    int enabled, pooling, limit, bse;
00221    unsigned int idlecheck;
00222    int connect = 0, res = 0;
00223 
00224    struct odbc_class *new;
00225 
00226    config = ast_config_load(cfg);
00227    if (!config) {
00228       ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00229       return -1;
00230    }
00231    for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00232       if (!strcasecmp(cat, "ENV")) {
00233          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00234             setenv(v->name, v->value, 1);
00235             ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00236          }
00237       } else {
00238          /* Reset all to defaults for each class of odbc connections */
00239          dsn = username = password = NULL;
00240          enabled = 1;
00241          connect = idlecheck = 0;
00242          pooling = 0;
00243          limit = 0;
00244          bse = 1;
00245          for (v = ast_variable_browse(config, cat); v; v = v->next) {
00246             if (!strcasecmp(v->name, "pooling")) {
00247                if (ast_true(v->value))
00248                   pooling = 1;
00249             } else if (!strcasecmp(v->name, "limit")) {
00250                sscanf(v->value, "%d", &limit);
00251                if (ast_true(v->value) && !limit) {
00252                   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);
00253                   limit = 1023;
00254                } else if (ast_false(v->value)) {
00255                   ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00256                   enabled = 0;
00257                   break;
00258                }
00259             } else if (!strcasecmp(v->name, "idlecheck")) {
00260                sscanf(v->value, "%d", &idlecheck);
00261             } else if (!strcasecmp(v->name, "enabled")) {
00262                enabled = ast_true(v->value);
00263             } else if (!strcasecmp(v->name, "pre-connect")) {
00264                connect = ast_true(v->value);
00265             } else if (!strcasecmp(v->name, "dsn")) {
00266                dsn = v->value;
00267             } else if (!strcasecmp(v->name, "username")) {
00268                username = v->value;
00269             } else if (!strcasecmp(v->name, "password")) {
00270                password = v->value;
00271             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00272                bse = ast_true(v->value);
00273             }
00274          }
00275 
00276          if (enabled && !ast_strlen_zero(dsn)) {
00277             new = ast_calloc(1, sizeof(*new));
00278 
00279             if (!new) {
00280                res = -1;
00281                break;
00282             }
00283 
00284             if (cat)
00285                ast_copy_string(new->name, cat, sizeof(new->name));
00286             if (dsn)
00287                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00288             if (username)
00289                ast_copy_string(new->username, username, sizeof(new->username));
00290             if (password)
00291                ast_copy_string(new->password, password, sizeof(new->password));
00292 
00293             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00294             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00295 
00296             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00297                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00298                SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00299                return res;
00300             }
00301 
00302             if (pooling) {
00303                new->haspool = pooling;
00304                if (limit) {
00305                   new->limit = limit;
00306                } else {
00307                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00308                   new->limit = 5;
00309                }
00310             }
00311 
00312             new->backslash_is_escape = bse ? 1 : 0;
00313             new->idlecheck = idlecheck;
00314 
00315             odbc_register_class(new, connect);
00316             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00317          }
00318       }
00319    }
00320    ast_config_destroy(config);
00321    return res;
00322 }
00323 
00324 static int odbc_show_command(int fd, int argc, char **argv)
00325 {
00326    struct odbc_class *class;
00327    struct odbc_obj *current;
00328 
00329    AST_LIST_LOCK(&odbc_list);
00330    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00331       if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) {
00332          int count = 0;
00333          ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn);
00334 
00335          if (class->haspool) {
00336             ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count);
00337 
00338             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00339                ast_cli(fd, "  Connection %d: %s\n", ++count, current->used ? "in use" : current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00340             }
00341          } else {
00342             /* Should only ever be one of these */
00343             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00344                ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no");
00345             }
00346          }
00347 
00348             ast_cli(fd, "\n");
00349       }
00350    }
00351    AST_LIST_UNLOCK(&odbc_list);
00352 
00353    return 0;
00354 }
00355 
00356 static char show_usage[] =
00357 "Usage: odbc show [<class>]\n"
00358 "       List settings of a particular ODBC class.\n"
00359 "       or, if not specified, all classes.\n";
00360 
00361 static struct ast_cli_entry cli_odbc[] = {
00362    { { "odbc", "show", NULL },
00363    odbc_show_command, "List ODBC DSN(s)",
00364    show_usage },
00365 };
00366 
00367 static int odbc_register_class(struct odbc_class *class, int connect)
00368 {
00369    struct odbc_obj *obj;
00370    if (class) {
00371       AST_LIST_LOCK(&odbc_list);
00372       AST_LIST_INSERT_HEAD(&odbc_list, class, list);
00373       AST_LIST_UNLOCK(&odbc_list);
00374 
00375       if (connect) {
00376          /* Request and release builds a connection */
00377          obj = ast_odbc_request_obj(class->name, 0);
00378          if (obj)
00379             ast_odbc_release_obj(obj);
00380       }
00381 
00382       return 0;
00383    } else {
00384       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00385       return -1;
00386    }
00387 }
00388 
00389 void ast_odbc_release_obj(struct odbc_obj *obj)
00390 {
00391    /* For pooled connections, this frees the connection to be
00392     * reused.  For non-pooled connections, it does nothing. */
00393    obj->used = 0;
00394 }
00395 
00396 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
00397 {
00398    return obj->parent->backslash_is_escape;
00399 }
00400 
00401 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
00402 {
00403    struct odbc_obj *obj = NULL;
00404    struct odbc_class *class;
00405 
00406    AST_LIST_LOCK(&odbc_list);
00407    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00408       if (!strcmp(class->name, name))
00409          break;
00410    }
00411    AST_LIST_UNLOCK(&odbc_list);
00412 
00413    if (!class)
00414       return NULL;
00415 
00416    AST_LIST_LOCK(&class->odbc_obj);
00417    if (class->haspool) {
00418       /* Recycle connections before building another */
00419       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00420          if (! obj->used) {
00421             obj->used = 1;
00422             break;
00423          }
00424       }
00425 
00426       if (!obj && (class->count < class->limit)) {
00427          class->count++;
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             class->count--;
00441          } else {
00442             obj->used = 1;
00443             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00444          }
00445       }
00446    } else {
00447       /* Non-pooled connection: multiple modules can use the same connection. */
00448       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00449          /* Non-pooled connection: if there is an entry, return it */
00450          break;
00451       }
00452 
00453       if (!obj) {
00454          /* No entry: build one */
00455          obj = ast_calloc(1, sizeof(*obj));
00456          if (!obj) {
00457             AST_LIST_UNLOCK(&class->odbc_obj);
00458             return NULL;
00459          }
00460          ast_mutex_init(&obj->lock);
00461          obj->parent = class;
00462          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00463             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00464             ast_mutex_destroy(&obj->lock);
00465             free(obj);
00466             obj = NULL;
00467          } else {
00468             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00469          }
00470       }
00471    }
00472    AST_LIST_UNLOCK(&class->odbc_obj);
00473 
00474    if (obj && check) {
00475       ast_odbc_sanity_check(obj);
00476    } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_ms(ast_tvnow(), obj->last_used) / 1000 > obj->parent->idlecheck)
00477       odbc_obj_connect(obj);
00478 
00479    return obj;
00480 }
00481 
00482 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
00483 {
00484    int res;
00485    SQLINTEGER err;
00486    short int mlen;
00487    unsigned char msg[200], stat[10];
00488 
00489    ast_mutex_lock(&obj->lock);
00490 
00491    res = SQLDisconnect(obj->con);
00492 
00493    if (res == ODBC_SUCCESS) {
00494       ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00495    } else {
00496       ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00497    }
00498 
00499    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == ODBC_SUCCESS)) {
00500       ast_log(LOG_DEBUG, "Database handle deallocated\n");
00501    } else {
00502       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00503       ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00504    }
00505 
00506    obj->up = 0;
00507    ast_mutex_unlock(&obj->lock);
00508    return ODBC_SUCCESS;
00509 }
00510 
00511 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
00512 {
00513    int res;
00514    SQLINTEGER err;
00515    short int mlen;
00516    unsigned char msg[200], stat[10];
00517 #ifdef NEEDTRACE
00518    SQLINTEGER enable = 1;
00519    char *tracefile = "/tmp/odbc.trace";
00520 #endif
00521    ast_mutex_lock(&obj->lock);
00522 
00523    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00524 
00525    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00526       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00527       ast_mutex_unlock(&obj->lock);
00528       return ODBC_FAIL;
00529    }
00530    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00531    SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00532 #ifdef NEEDTRACE
00533    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00534    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00535 #endif
00536 
00537    if (obj->up) {
00538       odbc_obj_disconnect(obj);
00539       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00540    } else {
00541       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00542    }
00543 
00544    res = SQLConnect(obj->con,
00545          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00546          (SQLCHAR *) obj->parent->username, SQL_NTS,
00547          (SQLCHAR *) obj->parent->password, SQL_NTS);
00548 
00549    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00550       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00551       ast_mutex_unlock(&obj->lock);
00552       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00553       return ODBC_FAIL;
00554    } else {
00555       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00556       obj->up = 1;
00557       obj->last_used = ast_tvnow();
00558    }
00559 
00560    ast_mutex_unlock(&obj->lock);
00561    return ODBC_SUCCESS;
00562 }
00563 
00564 static int reload(void)
00565 {
00566    static char *cfg = "res_odbc.conf";
00567    struct ast_config *config;
00568    struct ast_variable *v;
00569    char *cat, *dsn, *username, *password;
00570    int enabled, pooling, limit, bse;
00571    unsigned int idlecheck;
00572    int connect = 0, res = 0;
00573 
00574    struct odbc_class *new, *class;
00575    struct odbc_obj *current;
00576 
00577    /* First, mark all to be purged */
00578    AST_LIST_LOCK(&odbc_list);
00579    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00580       class->delme = 1;
00581    }
00582 
00583    config = ast_config_load(cfg);
00584    if (config) {
00585       for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00586          if (!strcasecmp(cat, "ENV")) {
00587             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00588                setenv(v->name, v->value, 1);
00589                ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00590             }
00591          } else {
00592             /* Reset all to defaults for each class of odbc connections */
00593             dsn = username = password = NULL;
00594             enabled = 1;
00595             connect = idlecheck = 0;
00596             pooling = 0;
00597             limit = 0;
00598             bse = 1;
00599             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00600                if (!strcasecmp(v->name, "pooling")) {
00601                   pooling = 1;
00602                } else if (!strcasecmp(v->name, "limit")) {
00603                   sscanf(v->value, "%d", &limit);
00604                   if (ast_true(v->value) && !limit) {
00605                      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);
00606                      limit = 1023;
00607                   } else if (ast_false(v->value)) {
00608                      ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00609                      enabled = 0;
00610                      break;
00611                   }
00612                } else if (!strcasecmp(v->name, "idlecheck")) {
00613                   sscanf(v->value, "%ud", &idlecheck);
00614                } else if (!strcasecmp(v->name, "enabled")) {
00615                   enabled = ast_true(v->value);
00616                } else if (!strcasecmp(v->name, "pre-connect")) {
00617                   connect = ast_true(v->value);
00618                } else if (!strcasecmp(v->name, "dsn")) {
00619                   dsn = v->value;
00620                } else if (!strcasecmp(v->name, "username")) {
00621                   username = v->value;
00622                } else if (!strcasecmp(v->name, "password")) {
00623                   password = v->value;
00624                } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00625                   bse = ast_true(v->value);
00626                }
00627             }
00628 
00629             if (enabled && !ast_strlen_zero(dsn)) {
00630                /* First, check the list to see if it already exists */
00631                AST_LIST_TRAVERSE(&odbc_list, class, list) {
00632                   if (!strcmp(class->name, cat)) {
00633                      class->delme = 0;
00634                      break;
00635                   }
00636                }
00637 
00638                if (class) {
00639                   new = class;
00640                } else {
00641                   new = ast_calloc(1, sizeof(*new));
00642                }
00643 
00644                if (!new) {
00645                   res = -1;
00646                   break;
00647                }
00648 
00649                if (cat)
00650                   ast_copy_string(new->name, cat, sizeof(new->name));
00651                if (dsn)
00652                   ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00653                if (username)
00654                   ast_copy_string(new->username, username, sizeof(new->username));
00655                if (password)
00656                   ast_copy_string(new->password, password, sizeof(new->password));
00657 
00658                if (!class) {
00659                   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00660                   res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00661 
00662                   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00663                      ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00664                      SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00665                      AST_LIST_UNLOCK(&odbc_list);
00666                      return res;
00667                   }
00668                }
00669 
00670                if (pooling) {
00671                   new->haspool = pooling;
00672                   if (limit) {
00673                      new->limit = limit;
00674                   } else {
00675                      ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00676                      new->limit = 5;
00677                   }
00678                }
00679 
00680                new->backslash_is_escape = bse;
00681                new->idlecheck = idlecheck;
00682 
00683                if (class) {
00684                   ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
00685                } else {
00686                   odbc_register_class(new, connect);
00687                   ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00688                }
00689             }
00690          }
00691       }
00692       ast_config_destroy(config);
00693    }
00694 
00695    /* Purge classes that we know can go away (pooled with 0, only) */
00696    AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
00697       if (class->delme && class->haspool && class->count == 0) {
00698          AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) {
00699             AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list);
00700             odbc_obj_disconnect(current);
00701             ast_mutex_destroy(&current->lock);
00702             free(current);
00703          }
00704          AST_LIST_TRAVERSE_SAFE_END;
00705 
00706          AST_LIST_REMOVE_CURRENT(&odbc_list, list);
00707          free(class);
00708       }
00709    }
00710    AST_LIST_TRAVERSE_SAFE_END;
00711    AST_LIST_UNLOCK(&odbc_list);
00712 
00713    return 0;
00714 }
00715 
00716 static int unload_module(void)
00717 {
00718    /* Prohibit unloading */
00719    return -1;
00720 }
00721 
00722 static int load_module(void)
00723 {
00724    if(load_odbc_config() == -1)
00725       return AST_MODULE_LOAD_DECLINE;
00726    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00727    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00728    return 0;
00729 }
00730 
00731 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Resource",
00732       .load = load_module,
00733       .unload = unload_module,
00734       .reload = reload,
00735           );

Generated on Mon Nov 24 15:34:20 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7