![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
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 ***/ 00035 00036 #include "asterisk.h" 00037 00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 52832 $") 00039 00040 #include <stdio.h> 00041 #include <stdlib.h> 00042 #include <unistd.h> 00043 #include <string.h> 00044 00045 #include "asterisk/file.h" 00046 #include "asterisk/logger.h" 00047 #include "asterisk/channel.h" 00048 #include "asterisk/config.h" 00049 #include "asterisk/options.h" 00050 #include "asterisk/pbx.h" 00051 #include "asterisk/module.h" 00052 #include "asterisk/cli.h" 00053 #include "asterisk/lock.h" 00054 #include "asterisk/res_odbc.h" 00055 00056 struct odbc_class 00057 { 00058 AST_LIST_ENTRY(odbc_class) list; 00059 char name[80]; 00060 char dsn[80]; 00061 char username[80]; 00062 char password[80]; 00063 char sanitysql[256]; 00064 SQLHENV env; 00065 unsigned int haspool:1; /* Boolean - TDS databases need this */ 00066 unsigned int limit:10; /* Gives a limit of 1023 maximum */ 00067 unsigned int count:10; /* Running count of pooled connections */ 00068 unsigned int delme:1; /* Purge the class */ 00069 AST_LIST_HEAD(, odbc_obj) odbc_obj; 00070 }; 00071 00072 AST_LIST_HEAD_STATIC(odbc_list, odbc_class); 00073 00074 static odbc_status odbc_obj_connect(struct odbc_obj *obj); 00075 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj); 00076 static int odbc_register_class(struct odbc_class *class, int connect); 00077 00078 00079 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data) 00080 { 00081 int res = 0, i, attempt; 00082 SQLINTEGER nativeerror=0, numfields=0; 00083 SQLSMALLINT diagbytes=0; 00084 unsigned char state[10], diagnostic[256]; 00085 SQLHSTMT stmt; 00086 00087 for (attempt = 0; attempt < 2; attempt++) { 00088 /* This prepare callback may do more than just prepare -- it may also 00089 * bind parameters, bind results, etc. The real key, here, is that 00090 * when we disconnect, all handles become invalid for most databases. 00091 * We must therefore redo everything when we establish a new 00092 * connection. */ 00093 stmt = prepare_cb(obj, data); 00094 00095 if (stmt) { 00096 res = SQLExecute(stmt); 00097 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00098 if (res == SQL_ERROR) { 00099 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00100 for (i=0; i< numfields + 1; i++) { 00101 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00102 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00103 if (i > 10) { 00104 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00105 break; 00106 } 00107 } 00108 } 00109 00110 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res); 00111 SQLFreeHandle(SQL_HANDLE_STMT, stmt); 00112 stmt = NULL; 00113 00114 obj->up = 0; 00115 /* 00116 * While this isn't the best way to try to correct an error, this won't automatically 00117 * fail when the statement handle invalidates. 00118 */ 00119 ast_odbc_sanity_check(obj); 00120 continue; 00121 } 00122 break; 00123 } else if (attempt == 0) 00124 ast_odbc_sanity_check(obj); 00125 } 00126 00127 return stmt; 00128 } 00129 00130 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) 00131 { 00132 int res = 0, i; 00133 SQLINTEGER nativeerror=0, numfields=0; 00134 SQLSMALLINT diagbytes=0; 00135 unsigned char state[10], diagnostic[256]; 00136 00137 res = SQLExecute(stmt); 00138 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00139 if (res == SQL_ERROR) { 00140 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00141 for (i=0; i< numfields + 1; i++) { 00142 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00143 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00144 if (i > 10) { 00145 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00146 break; 00147 } 00148 } 00149 } 00150 #if 0 00151 /* This is a really bad method of trying to correct a dead connection. It 00152 * only ever really worked with MySQL. It will not work with any other 00153 * database, since most databases prepare their statements on the server, 00154 * and if you disconnect, you invalidate the statement handle. Hence, if 00155 * you disconnect, you're going to fail anyway, whether you try to execute 00156 * a second time or not. 00157 */ 00158 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res); 00159 ast_mutex_lock(&obj->lock); 00160 obj->up = 0; 00161 ast_mutex_unlock(&obj->lock); 00162 odbc_obj_disconnect(obj); 00163 odbc_obj_connect(obj); 00164 res = SQLExecute(stmt); 00165 #endif 00166 } 00167 00168 return res; 00169 } 00170 00171 00172 int ast_odbc_sanity_check(struct odbc_obj *obj) 00173 { 00174 char *test_sql = "select 1"; 00175 SQLHSTMT stmt; 00176 int res = 0; 00177 00178 if (obj->parent->sanitysql) 00179 test_sql = obj->parent->sanitysql; 00180 00181 if (obj->up) { 00182 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00183 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00184 obj->up = 0; 00185 } else { 00186 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS); 00187 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00188 obj->up = 0; 00189 } else { 00190 res = SQLExecute(stmt); 00191 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00192 obj->up = 0; 00193 } 00194 } 00195 } 00196 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00197 } 00198 00199 if (!obj->up) { /* Try to reconnect! */ 00200 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n"); 00201 odbc_obj_disconnect(obj); 00202 odbc_obj_connect(obj); 00203 } 00204 return obj->up; 00205 } 00206 00207 static int load_odbc_config(void) 00208 { 00209 static char *cfg = "res_odbc.conf"; 00210 struct ast_config *config; 00211 struct ast_variable *v; 00212 char *cat, *dsn, *username, *password, *sanitysql; 00213 int enabled, pooling, limit; 00214 int connect = 0, res = 0; 00215 00216 struct odbc_class *new; 00217 00218 config = ast_config_load(cfg); 00219 if (!config) { 00220 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n"); 00221 return -1; 00222 } 00223 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) { 00224 if (!strcasecmp(cat, "ENV")) { 00225 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00226 setenv(v->name, v->value, 1); 00227 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); 00228 } 00229 } else { 00230 /* Reset all to defaults for each class of odbc connections */ 00231 dsn = username = password = sanitysql = NULL; 00232 enabled = 1; 00233 connect = 0; 00234 pooling = 0; 00235 limit = 0; 00236 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00237 if (!strcasecmp(v->name, "pooling")) { 00238 if (ast_true(v->value)) 00239 pooling = 1; 00240 } else if (!strcasecmp(v->name, "limit")) { 00241 sscanf(v->value, "%d", &limit); 00242 if (ast_true(v->value) && !limit) { 00243 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); 00244 limit = 1023; 00245 } else if (ast_false(v->value)) { 00246 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00247 enabled = 0; 00248 break; 00249 } 00250 } else if (!strcasecmp(v->name, "enabled")) { 00251 enabled = ast_true(v->value); 00252 } else if (!strcasecmp(v->name, "pre-connect")) { 00253 connect = ast_true(v->value); 00254 } else if (!strcasecmp(v->name, "dsn")) { 00255 dsn = v->value; 00256 } else if (!strcasecmp(v->name, "username")) { 00257 username = v->value; 00258 } else if (!strcasecmp(v->name, "password")) { 00259 password = v->value; 00260 } else if (!strcasecmp(v->name, "sanitysql")) { 00261 sanitysql = v->value; 00262 } 00263 } 00264 00265 if (enabled && !ast_strlen_zero(dsn)) { 00266 new = ast_calloc(1, sizeof(*new)); 00267 00268 if (!new) { 00269 res = -1; 00270 break; 00271 } 00272 00273 if (cat) 00274 ast_copy_string(new->name, cat, sizeof(new->name)); 00275 if (dsn) 00276 ast_copy_string(new->dsn, dsn, sizeof(new->dsn)); 00277 if (username) 00278 ast_copy_string(new->username, username, sizeof(new->username)); 00279 if (password) 00280 ast_copy_string(new->password, password, sizeof(new->password)); 00281 if (sanitysql) 00282 ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql)); 00283 00284 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00285 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00286 00287 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00288 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00289 SQLFreeHandle(SQL_HANDLE_ENV, new->env); 00290 return res; 00291 } 00292 00293 if (pooling) { 00294 new->haspool = pooling; 00295 if (limit) { 00296 new->limit = limit; 00297 } else { 00298 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00299 new->limit = 5; 00300 } 00301 } 00302 00303 odbc_register_class(new, connect); 00304 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00305 } 00306 } 00307 } 00308 ast_config_destroy(config); 00309 return res; 00310 } 00311 00312 static int odbc_show_command(int fd, int argc, char **argv) 00313 { 00314 struct odbc_class *class; 00315 struct odbc_obj *current; 00316 00317 AST_LIST_LOCK(&odbc_list); 00318 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00319 if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) { 00320 int count = 0; 00321 ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn); 00322 00323 if (class->haspool) { 00324 ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count); 00325 00326 AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) { 00327 ast_cli(fd, " Connection %d: %s", ++count, current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 00328 } 00329 } else { 00330 /* Should only ever be one of these */ 00331 AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) { 00332 ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no"); 00333 } 00334 } 00335 00336 ast_cli(fd, "\n"); 00337 } 00338 } 00339 AST_LIST_UNLOCK(&odbc_list); 00340 00341 return 0; 00342 } 00343 00344 static const char show_usage[] = 00345 "Usage: odbc show [<class>]\n" 00346 " List settings of a particular ODBC class.\n" 00347 " or, if not specified, all classes.\n"; 00348 00349 static struct ast_cli_entry cli_odbc[] = { 00350 { { "odbc", "show", NULL }, 00351 odbc_show_command, "List ODBC DSN(s)", 00352 show_usage }, 00353 }; 00354 00355 static int odbc_register_class(struct odbc_class *class, int connect) 00356 { 00357 struct odbc_obj *obj; 00358 if (class) { 00359 AST_LIST_LOCK(&odbc_list); 00360 AST_LIST_INSERT_HEAD(&odbc_list, class, list); 00361 AST_LIST_UNLOCK(&odbc_list); 00362 00363 if (connect) { 00364 /* Request and release builds a connection */ 00365 obj = ast_odbc_request_obj(class->name, 0); 00366 if (obj) 00367 ast_odbc_release_obj(obj); 00368 } 00369 00370 return 0; 00371 } else { 00372 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n"); 00373 return -1; 00374 } 00375 } 00376 00377 void ast_odbc_release_obj(struct odbc_obj *obj) 00378 { 00379 /* For pooled connections, this frees the connection to be 00380 * reused. For non-pooled connections, it does nothing. */ 00381 obj->used = 0; 00382 } 00383 00384 struct odbc_obj *ast_odbc_request_obj(const char *name, int check) 00385 { 00386 struct odbc_obj *obj = NULL; 00387 struct odbc_class *class; 00388 00389 AST_LIST_LOCK(&odbc_list); 00390 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00391 if (!strcmp(class->name, name)) 00392 break; 00393 } 00394 AST_LIST_UNLOCK(&odbc_list); 00395 00396 if (!class) 00397 return NULL; 00398 00399 AST_LIST_LOCK(&class->odbc_obj); 00400 if (class->haspool) { 00401 /* Recycle connections before building another */ 00402 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) { 00403 if (! obj->used) { 00404 obj->used = 1; 00405 break; 00406 } 00407 } 00408 00409 if (!obj && (class->count < class->limit)) { 00410 class->count++; 00411 obj = ast_calloc(1, sizeof(*obj)); 00412 if (!obj) { 00413 AST_LIST_UNLOCK(&class->odbc_obj); 00414 return NULL; 00415 } 00416 ast_mutex_init(&obj->lock); 00417 obj->parent = class; 00418 odbc_obj_connect(obj); 00419 AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list); 00420 } 00421 } else { 00422 /* Non-pooled connection: multiple modules can use the same connection. */ 00423 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) { 00424 /* Non-pooled connection: if there is an entry, return it */ 00425 break; 00426 } 00427 00428 if (!obj) { 00429 /* No entry: build one */ 00430 obj = ast_calloc(1, sizeof(*obj)); 00431 if (!obj) { 00432 AST_LIST_UNLOCK(&class->odbc_obj); 00433 return NULL; 00434 } 00435 ast_mutex_init(&obj->lock); 00436 obj->parent = class; 00437 if (odbc_obj_connect(obj) == ODBC_FAIL) { 00438 ast_log(LOG_WARNING, "Failed to connect to %s\n", name); 00439 ast_mutex_destroy(&obj->lock); 00440 free(obj); 00441 obj = NULL; 00442 } else { 00443 AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list); 00444 } 00445 } 00446 } 00447 AST_LIST_UNLOCK(&class->odbc_obj); 00448 00449 if (obj && check) { 00450 ast_odbc_sanity_check(obj); 00451 } 00452 return obj; 00453 } 00454 00455 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj) 00456 { 00457 int res; 00458 ast_mutex_lock(&obj->lock); 00459 00460 res = SQLDisconnect(obj->con); 00461 00462 if (res == ODBC_SUCCESS) { 00463 ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn); 00464 } else { 00465 ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n", 00466 obj->parent->name, obj->parent->dsn); 00467 } 00468 obj->up = 0; 00469 ast_mutex_unlock(&obj->lock); 00470 return ODBC_SUCCESS; 00471 } 00472 00473 static odbc_status odbc_obj_connect(struct odbc_obj *obj) 00474 { 00475 int res; 00476 SQLINTEGER err; 00477 short int mlen; 00478 unsigned char msg[200], stat[10]; 00479 #ifdef NEEDTRACE 00480 SQLINTEGER enable = 1; 00481 char *tracefile = "/tmp/odbc.trace"; 00482 #endif 00483 ast_mutex_lock(&obj->lock); 00484 00485 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con); 00486 00487 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00488 00489 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res); 00490 SQLFreeHandle(SQL_HANDLE_ENV, obj->parent->env); 00491 00492 ast_mutex_unlock(&obj->lock); 00493 return ODBC_FAIL; 00494 } 00495 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0); 00496 #ifdef NEEDTRACE 00497 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER); 00498 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile)); 00499 #endif 00500 00501 if (obj->up) { 00502 odbc_obj_disconnect(obj); 00503 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name); 00504 } else { 00505 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name); 00506 } 00507 00508 res = SQLConnect(obj->con, 00509 (SQLCHAR *) obj->parent->dsn, SQL_NTS, 00510 (SQLCHAR *) obj->parent->username, SQL_NTS, 00511 (SQLCHAR *) obj->parent->password, SQL_NTS); 00512 00513 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00514 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen); 00515 ast_mutex_unlock(&obj->lock); 00516 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg); 00517 return ODBC_FAIL; 00518 } else { 00519 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn); 00520 obj->up = 1; 00521 } 00522 00523 ast_mutex_unlock(&obj->lock); 00524 return ODBC_SUCCESS; 00525 } 00526 00527 static int reload(void) 00528 { 00529 static char *cfg = "res_odbc.conf"; 00530 struct ast_config *config; 00531 struct ast_variable *v; 00532 char *cat, *dsn, *username, *password, *sanitysql; 00533 int enabled, pooling, limit; 00534 int connect = 0, res = 0; 00535 00536 struct odbc_class *new, *class; 00537 struct odbc_obj *current; 00538 00539 /* First, mark all to be purged */ 00540 AST_LIST_LOCK(&odbc_list); 00541 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00542 class->delme = 1; 00543 } 00544 00545 config = ast_config_load(cfg); 00546 if (config) { 00547 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) { 00548 if (!strcasecmp(cat, "ENV")) { 00549 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00550 setenv(v->name, v->value, 1); 00551 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); 00552 } 00553 } else { 00554 /* Reset all to defaults for each class of odbc connections */ 00555 dsn = username = password = sanitysql = NULL; 00556 enabled = 1; 00557 connect = 0; 00558 pooling = 0; 00559 limit = 0; 00560 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00561 if (!strcasecmp(v->name, "pooling")) { 00562 pooling = 1; 00563 } else if (!strcasecmp(v->name, "limit")) { 00564 sscanf(v->value, "%d", &limit); 00565 if (ast_true(v->value) && !limit) { 00566 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); 00567 limit = 1023; 00568 } else if (ast_false(v->value)) { 00569 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00570 enabled = 0; 00571 break; 00572 } 00573 } else if (!strcasecmp(v->name, "enabled")) { 00574 enabled = ast_true(v->value); 00575 } else if (!strcasecmp(v->name, "pre-connect")) { 00576 connect = ast_true(v->value); 00577 } else if (!strcasecmp(v->name, "dsn")) { 00578 dsn = v->value; 00579 } else if (!strcasecmp(v->name, "username")) { 00580 username = v->value; 00581 } else if (!strcasecmp(v->name, "password")) { 00582 password = v->value; 00583 } else if (!strcasecmp(v->name, "sanitysql")) { 00584 sanitysql = v->value; 00585 } 00586 } 00587 00588 if (enabled && !ast_strlen_zero(dsn)) { 00589 /* First, check the list to see if it already exists */ 00590 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00591 if (!strcmp(class->name, cat)) { 00592 class->delme = 0; 00593 break; 00594 } 00595 } 00596 00597 if (class) { 00598 new = class; 00599 } else { 00600 new = ast_calloc(1, sizeof(*new)); 00601 } 00602 00603 if (!new) { 00604 res = -1; 00605 break; 00606 } 00607 00608 if (cat) 00609 ast_copy_string(new->name, cat, sizeof(new->name)); 00610 if (dsn) 00611 ast_copy_string(new->dsn, dsn, sizeof(new->dsn)); 00612 if (username) 00613 ast_copy_string(new->username, username, sizeof(new->username)); 00614 if (password) 00615 ast_copy_string(new->password, password, sizeof(new->password)); 00616 if (sanitysql) 00617 ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql)); 00618 00619 if (!class) { 00620 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00621 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00622 00623 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00624 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00625 SQLFreeHandle(SQL_HANDLE_ENV, new->env); 00626 AST_LIST_UNLOCK(&odbc_list); 00627 return res; 00628 } 00629 } 00630 00631 if (pooling) { 00632 new->haspool = pooling; 00633 if (limit) { 00634 new->limit = limit; 00635 } else { 00636 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00637 new->limit = 5; 00638 } 00639 } 00640 00641 if (class) { 00642 ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn); 00643 } else { 00644 odbc_register_class(new, connect); 00645 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00646 } 00647 } 00648 } 00649 } 00650 ast_config_destroy(config); 00651 } 00652 00653 /* Purge classes that we know can go away (pooled with 0, only) */ 00654 AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) { 00655 if (class->delme && class->haspool && class->count == 0) { 00656 AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) { 00657 AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list); 00658 odbc_obj_disconnect(current); 00659 ast_mutex_destroy(¤t->lock); 00660 free(current); 00661 } 00662 AST_LIST_TRAVERSE_SAFE_END; 00663 00664 AST_LIST_REMOVE_CURRENT(&odbc_list, list); 00665 free(class); 00666 } 00667 } 00668 AST_LIST_TRAVERSE_SAFE_END; 00669 AST_LIST_UNLOCK(&odbc_list); 00670 00671 return 0; 00672 } 00673 00674 static int unload_module(void) 00675 { 00676 /* Prohibit unloading */ 00677 return -1; 00678 } 00679 00680 static int load_module(void) 00681 { 00682 if(load_odbc_config() == -1) 00683 return AST_MODULE_LOAD_DECLINE; 00684 ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry)); 00685 ast_log(LOG_NOTICE, "res_odbc loaded.\n"); 00686 return 0; 00687 } 00688 00689 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Resource", 00690 .load = load_module, 00691 .unload = unload_module, 00692 .reload = reload, 00693 );