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: 145751 $")
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, "%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
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
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
00392
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
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
00448 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00449
00450 break;
00451 }
00452
00453 if (!obj) {
00454
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
00490 if (!obj->con) {
00491 return ODBC_SUCCESS;
00492 }
00493
00494 ast_mutex_lock(&obj->lock);
00495
00496 res = SQLDisconnect(obj->con);
00497
00498 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
00499 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00500 } else {
00501 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00502 }
00503
00504 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
00505 obj->con = NULL;
00506 ast_log(LOG_DEBUG, "Database handle deallocated\n");
00507 } else {
00508 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00509 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00510 }
00511
00512 obj->up = 0;
00513 ast_mutex_unlock(&obj->lock);
00514 return ODBC_SUCCESS;
00515 }
00516
00517 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
00518 {
00519 int res;
00520 SQLINTEGER err;
00521 short int mlen;
00522 unsigned char msg[200], stat[10];
00523 #ifdef NEEDTRACE
00524 SQLINTEGER enable = 1;
00525 char *tracefile = "/tmp/odbc.trace";
00526 #endif
00527 ast_mutex_lock(&obj->lock);
00528
00529 if (obj->up) {
00530 odbc_obj_disconnect(obj);
00531 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00532 } else {
00533 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00534 }
00535
00536 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00537
00538 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00539 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00540 ast_mutex_unlock(&obj->lock);
00541 return ODBC_FAIL;
00542 }
00543 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00544 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00545 #ifdef NEEDTRACE
00546 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00547 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00548 #endif
00549
00550 res = SQLConnect(obj->con,
00551 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00552 (SQLCHAR *) obj->parent->username, SQL_NTS,
00553 (SQLCHAR *) obj->parent->password, SQL_NTS);
00554
00555 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00556 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00557 ast_mutex_unlock(&obj->lock);
00558 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00559 return ODBC_FAIL;
00560 } else {
00561 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00562 obj->up = 1;
00563 obj->last_used = ast_tvnow();
00564 }
00565
00566 ast_mutex_unlock(&obj->lock);
00567 return ODBC_SUCCESS;
00568 }
00569
00570 static int reload(void)
00571 {
00572 static char *cfg = "res_odbc.conf";
00573 struct ast_config *config;
00574 struct ast_variable *v;
00575 char *cat, *dsn, *username, *password;
00576 int enabled, pooling, limit, bse;
00577 unsigned int idlecheck;
00578 int connect = 0, res = 0;
00579
00580 struct odbc_class *new, *class;
00581 struct odbc_obj *current;
00582
00583
00584 AST_LIST_LOCK(&odbc_list);
00585 AST_LIST_TRAVERSE(&odbc_list, class, list) {
00586 class->delme = 1;
00587 }
00588
00589 config = ast_config_load(cfg);
00590 if (config) {
00591 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00592 if (!strcasecmp(cat, "ENV")) {
00593 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00594 setenv(v->name, v->value, 1);
00595 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00596 }
00597 } else {
00598
00599 dsn = username = password = NULL;
00600 enabled = 1;
00601 connect = idlecheck = 0;
00602 pooling = 0;
00603 limit = 0;
00604 bse = 1;
00605 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00606 if (!strcasecmp(v->name, "pooling")) {
00607 pooling = 1;
00608 } else if (!strcasecmp(v->name, "limit")) {
00609 sscanf(v->value, "%d", &limit);
00610 if (ast_true(v->value) && !limit) {
00611 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);
00612 limit = 1023;
00613 } else if (ast_false(v->value)) {
00614 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
00615 enabled = 0;
00616 break;
00617 }
00618 } else if (!strcasecmp(v->name, "idlecheck")) {
00619 sscanf(v->value, "%ud", &idlecheck);
00620 } else if (!strcasecmp(v->name, "enabled")) {
00621 enabled = ast_true(v->value);
00622 } else if (!strcasecmp(v->name, "pre-connect")) {
00623 connect = ast_true(v->value);
00624 } else if (!strcasecmp(v->name, "dsn")) {
00625 dsn = v->value;
00626 } else if (!strcasecmp(v->name, "username")) {
00627 username = v->value;
00628 } else if (!strcasecmp(v->name, "password")) {
00629 password = v->value;
00630 } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00631 bse = ast_true(v->value);
00632 }
00633 }
00634
00635 if (enabled && !ast_strlen_zero(dsn)) {
00636
00637 AST_LIST_TRAVERSE(&odbc_list, class, list) {
00638 if (!strcmp(class->name, cat)) {
00639 class->delme = 0;
00640 break;
00641 }
00642 }
00643
00644 if (class) {
00645 new = class;
00646 } else {
00647 new = ast_calloc(1, sizeof(*new));
00648 }
00649
00650 if (!new) {
00651 res = -1;
00652 break;
00653 }
00654
00655 if (cat)
00656 ast_copy_string(new->name, cat, sizeof(new->name));
00657 if (dsn)
00658 ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00659 if (username)
00660 ast_copy_string(new->username, username, sizeof(new->username));
00661 if (password)
00662 ast_copy_string(new->password, password, sizeof(new->password));
00663
00664 if (!class) {
00665 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00666 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00667
00668 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00669 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00670 SQLFreeHandle(SQL_HANDLE_ENV, new->env);
00671 AST_LIST_UNLOCK(&odbc_list);
00672 return res;
00673 }
00674 }
00675
00676 if (pooling) {
00677 new->haspool = pooling;
00678 if (limit) {
00679 new->limit = limit;
00680 } else {
00681 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
00682 new->limit = 5;
00683 }
00684 }
00685
00686 new->backslash_is_escape = bse;
00687 new->idlecheck = idlecheck;
00688
00689 if (class) {
00690 ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
00691 } else {
00692 odbc_register_class(new, connect);
00693 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00694 }
00695 }
00696 }
00697 }
00698 ast_config_destroy(config);
00699 }
00700
00701
00702 AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
00703 if (class->delme && class->haspool && class->count == 0) {
00704 AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) {
00705 AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list);
00706 odbc_obj_disconnect(current);
00707 ast_mutex_destroy(¤t->lock);
00708 free(current);
00709 }
00710 AST_LIST_TRAVERSE_SAFE_END;
00711
00712 AST_LIST_REMOVE_CURRENT(&odbc_list, list);
00713 free(class);
00714 }
00715 }
00716 AST_LIST_TRAVERSE_SAFE_END;
00717 AST_LIST_UNLOCK(&odbc_list);
00718
00719 return 0;
00720 }
00721
00722 static int unload_module(void)
00723 {
00724
00725 return -1;
00726 }
00727
00728 static int load_module(void)
00729 {
00730 if(load_odbc_config() == -1)
00731 return AST_MODULE_LOAD_DECLINE;
00732 ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00733 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00734 return 0;
00735 }
00736
00737 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Resource",
00738 .load = load_module,
00739 .unload = unload_module,
00740 .reload = reload,
00741 );