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: 211528 $")
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! Attempting a reconnect...\n", res);
00114 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00115 stmt = NULL;
00116
00117 obj->up = 0;
00118
00119
00120
00121
00122
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
00161
00162
00163
00164
00165
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) {
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
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
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
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
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
00396
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
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
00452 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00453
00454 break;
00455 }
00456
00457 if (!obj) {
00458
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
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
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
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
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
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(¤t->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
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 );