Thu Oct 1 13:09:04 2009

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: 211596 $")
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, "%4d", &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                   /* Limit=no probably means "no limit", which is the maximum */
00256                   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);
00257                   limit = 1023;
00258                   break;
00259                } else if (limit > 1023) {
00260                   ast_log(LOG_WARNING, "Maximum limit in 1.4 is 1023.  Setting limit to 1023 for ODBC class '%s'.\n", cat);
00261                   limit = 1023;
00262                }
00263             } else if (!strcasecmp(v->name, "idlecheck")) {
00264                sscanf(v->value, "%30u", &idlecheck);
00265             } else if (!strcasecmp(v->name, "enabled")) {
00266                enabled = ast_true(v->value);
00267             } else if (!strcasecmp(v->name, "pre-connect")) {
00268                connect = ast_true(v->value);
00269             } else if (!strcasecmp(v->name, "dsn")) {
00270                dsn = v->value;
00271             } else if (!strcasecmp(v->name, "username")) {
00272                username = v->value;
00273             } else if (!strcasecmp(v->name, "password")) {
00274                password = v->value;
00275             } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00276                bse = ast_true(v->value);
00277             }
00278          }
00279 
00280          if (enabled && !ast_strlen_zero(dsn)) {
00281             new = ast_calloc(1, sizeof(*new));
00282 
00283             if (!new) {
00284                res = -1;
00285                break;
00286             }
00287 
00288             if (cat)
00289                ast_copy_string(new->name, cat, sizeof(new->name));
00290             if (dsn)
00291                ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00292             if (username)
00293                ast_copy_string(new->username, username, sizeof(new->username));
00294             if (password)
00295                ast_copy_string(new->password, password, sizeof(new->password));
00296 
00297             SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00298             res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00299 
00300             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00301                ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00302                SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00303                return res;
00304             }
00305 
00306             if (pooling) {
00307                new->haspool = pooling;
00308                if (limit) {
00309                   new->limit = limit;
00310                } else {
00311                   ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00312                   new->limit = 5;
00313                }
00314             }
00315 
00316             new->backslash_is_escape = bse ? 1 : 0;
00317             new->idlecheck = idlecheck;
00318 
00319             odbc_register_class(new, connect);
00320             ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00321          }
00322       }
00323    }
00324    ast_config_destroy(config);
00325    return res;
00326 }
00327 
00328 static int odbc_show_command(int fd, int argc, char **argv)
00329 {
00330    struct odbc_class *class;
00331    struct odbc_obj *current;
00332 
00333    AST_LIST_LOCK(&odbc_list);
00334    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00335       if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) {
00336          int count = 0;
00337          ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn);
00338 
00339          if (class->haspool) {
00340             ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count);
00341 
00342             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00343                ast_cli(fd, "  Connection %d: %s\n", ++count, current->used ? "in use" : current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00344             }
00345          } else {
00346             /* Should only ever be one of these */
00347             AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
00348                ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no");
00349             }
00350          }
00351 
00352             ast_cli(fd, "\n");
00353       }
00354    }
00355    AST_LIST_UNLOCK(&odbc_list);
00356 
00357    return 0;
00358 }
00359 
00360 static char show_usage[] =
00361 "Usage: odbc show [<class>]\n"
00362 "       List settings of a particular ODBC class.\n"
00363 "       or, if not specified, all classes.\n";
00364 
00365 static struct ast_cli_entry cli_odbc[] = {
00366    { { "odbc", "show", NULL },
00367    odbc_show_command, "List ODBC DSN(s)",
00368    show_usage },
00369 };
00370 
00371 static int odbc_register_class(struct odbc_class *class, int connect)
00372 {
00373    struct odbc_obj *obj;
00374    if (class) {
00375       AST_LIST_LOCK(&odbc_list);
00376       AST_LIST_INSERT_HEAD(&odbc_list, class, list);
00377       AST_LIST_UNLOCK(&odbc_list);
00378 
00379       if (connect) {
00380          /* Request and release builds a connection */
00381          obj = ast_odbc_request_obj(class->name, 0);
00382          if (obj)
00383             ast_odbc_release_obj(obj);
00384       }
00385 
00386       return 0;
00387    } else {
00388       ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00389       return -1;
00390    }
00391 }
00392 
00393 void ast_odbc_release_obj(struct odbc_obj *obj)
00394 {
00395    /* For pooled connections, this frees the connection to be
00396     * reused.  For non-pooled connections, it does nothing. */
00397    obj->used = 0;
00398 }
00399 
00400 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
00401 {
00402    return obj->parent->backslash_is_escape;
00403 }
00404 
00405 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
00406 {
00407    struct odbc_obj *obj = NULL;
00408    struct odbc_class *class;
00409 
00410    AST_LIST_LOCK(&odbc_list);
00411    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00412       if (!strcmp(class->name, name))
00413          break;
00414    }
00415    AST_LIST_UNLOCK(&odbc_list);
00416 
00417    if (!class)
00418       return NULL;
00419 
00420    AST_LIST_LOCK(&class->odbc_obj);
00421    if (class->haspool) {
00422       /* Recycle connections before building another */
00423       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00424          if (! obj->used) {
00425             obj->used = 1;
00426             break;
00427          }
00428       }
00429 
00430       if (!obj && (class->count < class->limit)) {
00431          class->count++;
00432          obj = ast_calloc(1, sizeof(*obj));
00433          if (!obj) {
00434             AST_LIST_UNLOCK(&class->odbc_obj);
00435             return NULL;
00436          }
00437          ast_mutex_init(&obj->lock);
00438          obj->parent = class;
00439          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00440             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00441             ast_mutex_destroy(&obj->lock);
00442             free(obj);
00443             obj = NULL;
00444             class->count--;
00445          } else {
00446             obj->used = 1;
00447             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00448          }
00449       }
00450    } else {
00451       /* Non-pooled connection: multiple modules can use the same connection. */
00452       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00453          /* Non-pooled connection: if there is an entry, return it */
00454          break;
00455       }
00456 
00457       if (!obj) {
00458          /* No entry: build one */
00459          obj = ast_calloc(1, sizeof(*obj));
00460          if (!obj) {
00461             AST_LIST_UNLOCK(&class->odbc_obj);
00462             return NULL;
00463          }
00464          ast_mutex_init(&obj->lock);
00465          obj->parent = class;
00466          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00467             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00468             ast_mutex_destroy(&obj->lock);
00469             free(obj);
00470             obj = NULL;
00471          } else {
00472             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00473          }
00474       }
00475    }
00476    AST_LIST_UNLOCK(&class->odbc_obj);
00477 
00478    if (obj && check) {
00479       ast_odbc_sanity_check(obj);
00480    } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_ms(ast_tvnow(), obj->last_used) / 1000 > obj->parent->idlecheck)
00481       odbc_obj_connect(obj);
00482 
00483    return obj;
00484 }
00485 
00486 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
00487 {
00488    int res;
00489    SQLINTEGER err;
00490    short int mlen;
00491    unsigned char msg[200], stat[10];
00492 
00493    /* Nothing to disconnect */
00494    if (!obj->con) {
00495       return ODBC_SUCCESS;
00496    }
00497 
00498    ast_mutex_lock(&obj->lock);
00499 
00500    res = SQLDisconnect(obj->con);
00501 
00502    if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
00503       ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00504    } else {
00505       ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00506    }
00507 
00508    if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
00509       obj->con = NULL;
00510       ast_log(LOG_DEBUG, "Database handle deallocated\n");
00511    } else {
00512       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00513       ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00514    }
00515 
00516    obj->up = 0;
00517    ast_mutex_unlock(&obj->lock);
00518    return ODBC_SUCCESS;
00519 }
00520 
00521 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
00522 {
00523    int res;
00524    SQLINTEGER err;
00525    short int mlen;
00526    unsigned char msg[200], stat[10];
00527 #ifdef NEEDTRACE
00528    SQLINTEGER enable = 1;
00529    char *tracefile = "/tmp/odbc.trace";
00530 #endif
00531    ast_mutex_lock(&obj->lock);
00532 
00533    if (obj->up) {
00534       odbc_obj_disconnect(obj);
00535       ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00536    } else {
00537       ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00538    }
00539 
00540    res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00541 
00542    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00543       ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00544       ast_mutex_unlock(&obj->lock);
00545       return ODBC_FAIL;
00546    }
00547    SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00548    SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00549 #ifdef NEEDTRACE
00550    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00551    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00552 #endif
00553 
00554    res = SQLConnect(obj->con,
00555          (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00556          (SQLCHAR *) obj->parent->username, SQL_NTS,
00557          (SQLCHAR *) obj->parent->password, SQL_NTS);
00558 
00559    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00560       SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00561       ast_mutex_unlock(&obj->lock);
00562       ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00563       return ODBC_FAIL;
00564    } else {
00565       ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00566       obj->up = 1;
00567       obj->last_used = ast_tvnow();
00568    }
00569 
00570    ast_mutex_unlock(&obj->lock);
00571    return ODBC_SUCCESS;
00572 }
00573 
00574 static int reload(void)
00575 {
00576    static char *cfg = "res_odbc.conf";
00577    struct ast_config *config;
00578    struct ast_variable *v;
00579    char *cat, *dsn, *username, *password;
00580    int enabled, pooling, limit, bse;
00581    unsigned int idlecheck;
00582    int connect = 0, res = 0;
00583 
00584    struct odbc_class *new, *class;
00585    struct odbc_obj *current;
00586 
00587    /* First, mark all to be purged */
00588    AST_LIST_LOCK(&odbc_list);
00589    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00590       class->delme = 1;
00591    }
00592 
00593    config = ast_config_load(cfg);
00594    if (config) {
00595       for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00596          if (!strcasecmp(cat, "ENV")) {
00597             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00598                setenv(v->name, v->value, 1);
00599                ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00600             }
00601          } else {
00602             /* Reset all to defaults for each class of odbc connections */
00603             dsn = username = password = NULL;
00604             enabled = 1;
00605             connect = idlecheck = 0;
00606             pooling = 0;
00607             limit = 0;
00608             bse = 1;
00609             for (v = ast_variable_browse(config, cat); v; v = v->next) {
00610                if (!strcasecmp(v->name, "pooling")) {
00611                   pooling = 1;
00612                } else if (!strcasecmp(v->name, "limit")) {
00613                   sscanf(v->value, "%4d", &limit);
00614                   if (ast_true(v->value) && !limit) {
00615                      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);
00616                      limit = 1023;
00617                   } else if (ast_false(v->value)) {
00618                      ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
00619                      enabled = 0;
00620                      break;
00621                   }
00622                } else if (!strcasecmp(v->name, "idlecheck")) {
00623                   sscanf(v->value, "%30u", &idlecheck);
00624                } else if (!strcasecmp(v->name, "enabled")) {
00625                   enabled = ast_true(v->value);
00626                } else if (!strcasecmp(v->name, "pre-connect")) {
00627                   connect = ast_true(v->value);
00628                } else if (!strcasecmp(v->name, "dsn")) {
00629                   dsn = v->value;
00630                } else if (!strcasecmp(v->name, "username")) {
00631                   username = v->value;
00632                } else if (!strcasecmp(v->name, "password")) {
00633                   password = v->value;
00634                } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00635                   bse = ast_true(v->value);
00636                }
00637             }
00638 
00639             if (enabled && !ast_strlen_zero(dsn)) {
00640                /* First, check the list to see if it already exists */
00641                AST_LIST_TRAVERSE(&odbc_list, class, list) {
00642                   if (!strcmp(class->name, cat)) {
00643                      class->delme = 0;
00644                      break;
00645                   }
00646                }
00647 
00648                if (class) {
00649                   new = class;
00650                } else {
00651                   new = ast_calloc(1, sizeof(*new));
00652                }
00653 
00654                if (!new) {
00655                   res = -1;
00656                   break;
00657                }
00658 
00659                if (cat)
00660                   ast_copy_string(new->name, cat, sizeof(new->name));
00661                if (dsn)
00662                   ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00663                if (username)
00664                   ast_copy_string(new->username, username, sizeof(new->username));
00665                if (password)
00666                   ast_copy_string(new->password, password, sizeof(new->password));
00667 
00668                if (!class) {
00669                   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00670                   res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00671 
00672                   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00673                      ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00674                      SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00675                      AST_LIST_UNLOCK(&odbc_list);
00676                      return res;
00677                   }
00678                }
00679 
00680                if (pooling) {
00681                   new->haspool = pooling;
00682                   if (limit) {
00683                      new->limit = limit;
00684                   } else {
00685                      ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
00686                      new->limit = 5;
00687                   }
00688                }
00689 
00690                new->backslash_is_escape = bse;
00691                new->idlecheck = idlecheck;
00692 
00693                if (class) {
00694                   ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
00695                } else {
00696                   odbc_register_class(new, connect);
00697                   ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00698                }
00699             }
00700          }
00701       }
00702       ast_config_destroy(config);
00703    }
00704 
00705    /* Purge classes that we know can go away (pooled with 0, only) */
00706    AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
00707       if (class->delme && class->haspool && class->count == 0) {
00708          AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) {
00709             AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list);
00710             odbc_obj_disconnect(current);
00711             ast_mutex_destroy(&current->lock);
00712             free(current);
00713          }
00714          AST_LIST_TRAVERSE_SAFE_END;
00715 
00716          AST_LIST_REMOVE_CURRENT(&odbc_list, list);
00717          free(class);
00718       }
00719    }
00720    AST_LIST_TRAVERSE_SAFE_END;
00721    AST_LIST_UNLOCK(&odbc_list);
00722 
00723    return 0;
00724 }
00725 
00726 static int unload_module(void)
00727 {
00728    /* Prohibit unloading */
00729    return -1;
00730 }
00731 
00732 static int load_module(void)
00733 {
00734    if(load_odbc_config() == -1)
00735       return AST_MODULE_LOAD_DECLINE;
00736    ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00737    ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00738    return 0;
00739 }
00740 
00741 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Resource",
00742       .load = load_module,
00743       .unload = unload_module,
00744       .reload = reload,
00745           );

Generated on Thu Oct 1 13:09:04 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7