00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
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;
00067 unsigned int limit:10;
00068 unsigned int count:10;
00069 unsigned int delme:1;
00070 unsigned int backslash_is_escape:1;
00071 unsigned int idlecheck;
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
00092
00093
00094
00095
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
00155
00156
00157
00158
00159
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) {
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
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
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
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
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
00390
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
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
00446 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00447
00448 break;
00449 }
00450
00451 if (!obj) {
00452
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
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
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
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
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
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(¤t->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
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 );