#include "asterisk.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/options.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/res_odbc.h"
Go to the source code of this file.
Data Structures | |
struct | odbc_class |
Functions | |
AST_LIST_HEAD_STATIC (odbc_list, odbc_class) | |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,"ODBC Resource",.load=load_module,.unload=unload_module,.reload=reload,) | |
SQLHSTMT | ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data) |
Prepares, executes, and returns the resulting statement handle. | |
void | ast_odbc_release_obj (struct odbc_obj *obj) |
Releases an ODBC object previously allocated by odbc_request_obj(). | |
struct odbc_obj * | ast_odbc_request_obj (const char *name, int check) |
Retrieves a connected ODBC object. | |
int | ast_odbc_sanity_check (struct odbc_obj *obj) |
Checks an ODBC object to ensure it is still connected. | |
int | ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt) |
Executes a prepared statement handle. | |
static int | load_module (void) |
static int | load_odbc_config (void) |
static odbc_status | odbc_obj_connect (struct odbc_obj *obj) |
static odbc_status | odbc_obj_disconnect (struct odbc_obj *obj) |
static int | odbc_register_class (struct odbc_class *class, int connect) |
static int | odbc_show_command (int fd, int argc, char **argv) |
static int | reload (void) |
static int | unload_module (void) |
Variables | |
static struct ast_cli_entry | cli_odbc [] |
static char | show_usage [] |
Definition in file res_odbc.c.
AST_LIST_HEAD_STATIC | ( | odbc_list | , | |
odbc_class | ||||
) |
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_GLOBAL_SYMBOLS | , | |||
"ODBC Resource" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload | |||
) |
SQLHSTMT ast_odbc_prepare_and_execute | ( | struct odbc_obj * | obj, | |
SQLHSTMT(*)(struct odbc_obj *obj, void *data) | prepare_cb, | |||
void * | data | |||
) |
Prepares, executes, and returns the resulting statement handle.
obj | The ODBC object | |
prepare_cb | A function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound. | |
data | A parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared. |
Definition at line 79 of file res_odbc.c.
References ast_log(), LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.
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; 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 /* XXX Actually, it might, if we're using a non-pooled connection. Possible race here. XXX */ 00120 odbc_obj_disconnect(obj); 00121 odbc_obj_connect(obj); 00122 continue; 00123 } 00124 break; 00125 } else { 00126 ast_log(LOG_WARNING, "SQL Prepare failed. Attempting a reconnect...\n"); 00127 odbc_obj_disconnect(obj); 00128 odbc_obj_connect(obj); 00129 } 00130 } 00131 00132 return stmt; 00133 }
void ast_odbc_release_obj | ( | struct odbc_obj * | obj | ) |
Releases an ODBC object previously allocated by odbc_request_obj().
obj | The ODBC object |
Definition at line 375 of file res_odbc.c.
References odbc_obj::used.
00376 { 00377 /* For pooled connections, this frees the connection to be 00378 * reused. For non-pooled connections, it does nothing. */ 00379 obj->used = 0; 00380 }
struct odbc_obj* ast_odbc_request_obj | ( | const char * | name, | |
int | check | |||
) | [read] |
Retrieves a connected ODBC object.
name | The name of the ODBC class for which a connection is needed. | |
check | Whether to ensure that a connection is valid before returning the handle. Usually unnecessary. |
Definition at line 382 of file res_odbc.c.
References ast_calloc, AST_LIST_INSERT_HEAD, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_odbc_sanity_check(), free, odbc_obj::lock, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), odbc_obj::parent, and odbc_obj::used.
00383 { 00384 struct odbc_obj *obj = NULL; 00385 struct odbc_class *class; 00386 00387 AST_LIST_LOCK(&odbc_list); 00388 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00389 if (!strcmp(class->name, name)) 00390 break; 00391 } 00392 AST_LIST_UNLOCK(&odbc_list); 00393 00394 if (!class) 00395 return NULL; 00396 00397 AST_LIST_LOCK(&class->odbc_obj); 00398 if (class->haspool) { 00399 /* Recycle connections before building another */ 00400 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) { 00401 if (! obj->used) { 00402 obj->used = 1; 00403 break; 00404 } 00405 } 00406 00407 if (!obj && (class->count < class->limit)) { 00408 class->count++; 00409 obj = ast_calloc(1, sizeof(*obj)); 00410 if (!obj) { 00411 AST_LIST_UNLOCK(&class->odbc_obj); 00412 return NULL; 00413 } 00414 ast_mutex_init(&obj->lock); 00415 obj->parent = class; 00416 odbc_obj_connect(obj); 00417 AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list); 00418 } 00419 } else { 00420 /* Non-pooled connection: multiple modules can use the same connection. */ 00421 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) { 00422 /* Non-pooled connection: if there is an entry, return it */ 00423 break; 00424 } 00425 00426 if (!obj) { 00427 /* No entry: build one */ 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 } else { 00441 AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list); 00442 } 00443 } 00444 } 00445 AST_LIST_UNLOCK(&class->odbc_obj); 00446 00447 if (obj && check) { 00448 ast_odbc_sanity_check(obj); 00449 } 00450 return obj; 00451 }
int ast_odbc_sanity_check | ( | struct odbc_obj * | obj | ) |
Checks an ODBC object to ensure it is still connected.
obj | The ODBC object |
Definition at line 177 of file res_odbc.c.
References ast_log(), odbc_obj::con, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.
00178 { 00179 char *test_sql = "select 1"; 00180 SQLHSTMT stmt; 00181 int res = 0; 00182 00183 if (obj->up) { 00184 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); 00185 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00186 obj->up = 0; 00187 } else { 00188 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS); 00189 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00190 obj->up = 0; 00191 } else { 00192 res = SQLExecute(stmt); 00193 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00194 obj->up = 0; 00195 } 00196 } 00197 } 00198 SQLFreeHandle (SQL_HANDLE_STMT, stmt); 00199 } 00200 00201 if (!obj->up) { /* Try to reconnect! */ 00202 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n"); 00203 odbc_obj_disconnect(obj); 00204 odbc_obj_connect(obj); 00205 } 00206 return obj->up; 00207 }
int ast_odbc_smart_execute | ( | struct odbc_obj * | obj, | |
SQLHSTMT | stmt | |||
) |
Executes a prepared statement handle.
obj | The non-NULL result of odbc_request_obj() | |
stmt | The prepared statement handle |
This function really only ever worked with MySQL, where the statement handle is not prepared on the server. If you are not using MySQL, you should avoid it.
Definition at line 135 of file res_odbc.c.
References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::lock, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.
00136 { 00137 int res = 0, i; 00138 SQLINTEGER nativeerror=0, numfields=0; 00139 SQLSMALLINT diagbytes=0; 00140 unsigned char state[10], diagnostic[256]; 00141 00142 res = SQLExecute(stmt); 00143 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) { 00144 if (res == SQL_ERROR) { 00145 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); 00146 for (i = 0; i < numfields; i++) { 00147 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); 00148 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); 00149 if (i > 10) { 00150 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); 00151 break; 00152 } 00153 } 00154 } 00155 #if 0 00156 /* This is a really bad method of trying to correct a dead connection. It 00157 * only ever really worked with MySQL. It will not work with any other 00158 * database, since most databases prepare their statements on the server, 00159 * and if you disconnect, you invalidate the statement handle. Hence, if 00160 * you disconnect, you're going to fail anyway, whether you try to execute 00161 * a second time or not. 00162 */ 00163 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res); 00164 ast_mutex_lock(&obj->lock); 00165 obj->up = 0; 00166 ast_mutex_unlock(&obj->lock); 00167 odbc_obj_disconnect(obj); 00168 odbc_obj_connect(obj); 00169 res = SQLExecute(stmt); 00170 #endif 00171 } 00172 00173 return res; 00174 }
static int load_module | ( | void | ) | [static] |
Definition at line 671 of file res_odbc.c.
References ast_cli_register_multiple(), ast_log(), AST_MODULE_LOAD_DECLINE, load_odbc_config(), and LOG_NOTICE.
00672 { 00673 if(load_odbc_config() == -1) 00674 return AST_MODULE_LOAD_DECLINE; 00675 ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry)); 00676 ast_log(LOG_NOTICE, "res_odbc loaded.\n"); 00677 return 0; 00678 }
static int load_odbc_config | ( | void | ) | [static] |
Definition at line 209 of file res_odbc.c.
References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_false(), ast_log(), ast_strlen_zero(), ast_true(), ast_variable_browse(), config, dsn, enabled, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, odbc_register_class(), password, setenv(), username, and ast_variable::value.
Referenced by load_module().
00210 { 00211 static char *cfg = "res_odbc.conf"; 00212 struct ast_config *config; 00213 struct ast_variable *v; 00214 char *cat, *dsn, *username, *password; 00215 int enabled, pooling, limit; 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 /* Reset all to defaults for each class of odbc connections */ 00233 dsn = username = password = NULL; 00234 enabled = 1; 00235 connect = 0; 00236 pooling = 0; 00237 limit = 0; 00238 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00239 if (!strcasecmp(v->name, "pooling")) { 00240 if (ast_true(v->value)) 00241 pooling = 1; 00242 } else if (!strcasecmp(v->name, "limit")) { 00243 sscanf(v->value, "%d", &limit); 00244 if (ast_true(v->value) && !limit) { 00245 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); 00246 limit = 1023; 00247 } else if (ast_false(v->value)) { 00248 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00249 enabled = 0; 00250 break; 00251 } 00252 } else if (!strcasecmp(v->name, "enabled")) { 00253 enabled = ast_true(v->value); 00254 } else if (!strcasecmp(v->name, "pre-connect")) { 00255 connect = ast_true(v->value); 00256 } else if (!strcasecmp(v->name, "dsn")) { 00257 dsn = v->value; 00258 } else if (!strcasecmp(v->name, "username")) { 00259 username = v->value; 00260 } else if (!strcasecmp(v->name, "password")) { 00261 password = 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 00282 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00283 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00284 00285 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00286 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00287 SQLFreeHandle(SQL_HANDLE_ENV, new->env); 00288 return res; 00289 } 00290 00291 if (pooling) { 00292 new->haspool = pooling; 00293 if (limit) { 00294 new->limit = limit; 00295 } else { 00296 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00297 new->limit = 5; 00298 } 00299 } 00300 00301 odbc_register_class(new, connect); 00302 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00303 } 00304 } 00305 } 00306 ast_config_destroy(config); 00307 return res; 00308 }
static odbc_status odbc_obj_connect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 471 of file res_odbc.c.
References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, ODBC_FAIL, odbc_obj_disconnect(), ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.
Referenced by ast_odbc_prepare_and_execute(), ast_odbc_request_obj(), ast_odbc_sanity_check(), and ast_odbc_smart_execute().
00472 { 00473 int res; 00474 SQLINTEGER err; 00475 short int mlen; 00476 unsigned char msg[200], stat[10]; 00477 #ifdef NEEDTRACE 00478 SQLINTEGER enable = 1; 00479 char *tracefile = "/tmp/odbc.trace"; 00480 #endif 00481 ast_mutex_lock(&obj->lock); 00482 00483 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con); 00484 00485 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00486 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res); 00487 ast_mutex_unlock(&obj->lock); 00488 return ODBC_FAIL; 00489 } 00490 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0); 00491 #ifdef NEEDTRACE 00492 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER); 00493 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile)); 00494 #endif 00495 00496 if (obj->up) { 00497 odbc_obj_disconnect(obj); 00498 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name); 00499 } else { 00500 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name); 00501 } 00502 00503 res = SQLConnect(obj->con, 00504 (SQLCHAR *) obj->parent->dsn, SQL_NTS, 00505 (SQLCHAR *) obj->parent->username, SQL_NTS, 00506 (SQLCHAR *) obj->parent->password, SQL_NTS); 00507 00508 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00509 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen); 00510 ast_mutex_unlock(&obj->lock); 00511 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg); 00512 return ODBC_FAIL; 00513 } else { 00514 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn); 00515 obj->up = 1; 00516 } 00517 00518 ast_mutex_unlock(&obj->lock); 00519 return ODBC_SUCCESS; 00520 }
static odbc_status odbc_obj_disconnect | ( | struct odbc_obj * | obj | ) | [static] |
Definition at line 453 of file res_odbc.c.
References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::con, odbc_obj::lock, LOG_WARNING, ODBC_SUCCESS, odbc_obj::parent, and odbc_obj::up.
Referenced by ast_odbc_prepare_and_execute(), ast_odbc_sanity_check(), ast_odbc_smart_execute(), odbc_obj_connect(), and reload().
00454 { 00455 int res; 00456 ast_mutex_lock(&obj->lock); 00457 00458 res = SQLDisconnect(obj->con); 00459 00460 if (res == ODBC_SUCCESS) { 00461 ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn); 00462 } else { 00463 ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n", 00464 obj->parent->name, obj->parent->dsn); 00465 } 00466 obj->up = 0; 00467 ast_mutex_unlock(&obj->lock); 00468 return ODBC_SUCCESS; 00469 }
static int odbc_register_class | ( | struct odbc_class * | class, | |
int | connect | |||
) | [static] |
Definition at line 353 of file res_odbc.c.
References AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_odbc_release_obj(), ast_odbc_request_obj(), and LOG_WARNING.
Referenced by load_odbc_config(), and reload().
00354 { 00355 struct odbc_obj *obj; 00356 if (class) { 00357 AST_LIST_LOCK(&odbc_list); 00358 AST_LIST_INSERT_HEAD(&odbc_list, class, list); 00359 AST_LIST_UNLOCK(&odbc_list); 00360 00361 if (connect) { 00362 /* Request and release builds a connection */ 00363 obj = ast_odbc_request_obj(class->name, 0); 00364 if (obj) 00365 ast_odbc_release_obj(obj); 00366 } 00367 00368 return 0; 00369 } else { 00370 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n"); 00371 return -1; 00372 } 00373 }
static int odbc_show_command | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 310 of file res_odbc.c.
References ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_odbc_sanity_check(), and odbc_obj::up.
00311 { 00312 struct odbc_class *class; 00313 struct odbc_obj *current; 00314 00315 AST_LIST_LOCK(&odbc_list); 00316 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00317 if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) { 00318 int count = 0; 00319 ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn); 00320 00321 if (class->haspool) { 00322 ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count); 00323 00324 AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) { 00325 ast_cli(fd, " Connection %d: %s\n", ++count, current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); 00326 } 00327 } else { 00328 /* Should only ever be one of these */ 00329 AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) { 00330 ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no"); 00331 } 00332 } 00333 00334 ast_cli(fd, "\n"); 00335 } 00336 } 00337 AST_LIST_UNLOCK(&odbc_list); 00338 00339 return 0; 00340 }
static int reload | ( | void | ) | [static] |
Definition at line 522 of file res_odbc.c.
References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_false(), AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy(), ast_strlen_zero(), ast_true(), ast_variable_browse(), config, dsn, enabled, free, odbc_obj::lock, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, odbc_obj_disconnect(), odbc_register_class(), password, setenv(), username, and ast_variable::value.
00523 { 00524 static char *cfg = "res_odbc.conf"; 00525 struct ast_config *config; 00526 struct ast_variable *v; 00527 char *cat, *dsn, *username, *password; 00528 int enabled, pooling, limit; 00529 int connect = 0, res = 0; 00530 00531 struct odbc_class *new, *class; 00532 struct odbc_obj *current; 00533 00534 /* First, mark all to be purged */ 00535 AST_LIST_LOCK(&odbc_list); 00536 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00537 class->delme = 1; 00538 } 00539 00540 config = ast_config_load(cfg); 00541 if (config) { 00542 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) { 00543 if (!strcasecmp(cat, "ENV")) { 00544 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00545 setenv(v->name, v->value, 1); 00546 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); 00547 } 00548 } else { 00549 /* Reset all to defaults for each class of odbc connections */ 00550 dsn = username = password = NULL; 00551 enabled = 1; 00552 connect = 0; 00553 pooling = 0; 00554 limit = 0; 00555 for (v = ast_variable_browse(config, cat); v; v = v->next) { 00556 if (!strcasecmp(v->name, "pooling")) { 00557 pooling = 1; 00558 } else if (!strcasecmp(v->name, "limit")) { 00559 sscanf(v->value, "%d", &limit); 00560 if (ast_true(v->value) && !limit) { 00561 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); 00562 limit = 1023; 00563 } else if (ast_false(v->value)) { 00564 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat); 00565 enabled = 0; 00566 break; 00567 } 00568 } else if (!strcasecmp(v->name, "enabled")) { 00569 enabled = ast_true(v->value); 00570 } else if (!strcasecmp(v->name, "pre-connect")) { 00571 connect = ast_true(v->value); 00572 } else if (!strcasecmp(v->name, "dsn")) { 00573 dsn = v->value; 00574 } else if (!strcasecmp(v->name, "username")) { 00575 username = v->value; 00576 } else if (!strcasecmp(v->name, "password")) { 00577 password = v->value; 00578 } 00579 } 00580 00581 if (enabled && !ast_strlen_zero(dsn)) { 00582 /* First, check the list to see if it already exists */ 00583 AST_LIST_TRAVERSE(&odbc_list, class, list) { 00584 if (!strcmp(class->name, cat)) { 00585 class->delme = 0; 00586 break; 00587 } 00588 } 00589 00590 if (class) { 00591 new = class; 00592 } else { 00593 new = ast_calloc(1, sizeof(*new)); 00594 } 00595 00596 if (!new) { 00597 res = -1; 00598 break; 00599 } 00600 00601 if (cat) 00602 ast_copy_string(new->name, cat, sizeof(new->name)); 00603 if (dsn) 00604 ast_copy_string(new->dsn, dsn, sizeof(new->dsn)); 00605 if (username) 00606 ast_copy_string(new->username, username, sizeof(new->username)); 00607 if (password) 00608 ast_copy_string(new->password, password, sizeof(new->password)); 00609 00610 if (!class) { 00611 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env); 00612 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); 00613 00614 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { 00615 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n"); 00616 SQLFreeHandle(SQL_HANDLE_ENV, new->env); 00617 AST_LIST_UNLOCK(&odbc_list); 00618 return res; 00619 } 00620 } 00621 00622 if (pooling) { 00623 new->haspool = pooling; 00624 if (limit) { 00625 new->limit = limit; 00626 } else { 00627 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n"); 00628 new->limit = 5; 00629 } 00630 } 00631 00632 if (class) { 00633 ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn); 00634 } else { 00635 odbc_register_class(new, connect); 00636 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn); 00637 } 00638 } 00639 } 00640 } 00641 ast_config_destroy(config); 00642 } 00643 00644 /* Purge classes that we know can go away (pooled with 0, only) */ 00645 AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) { 00646 if (class->delme && class->haspool && class->count == 0) { 00647 AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) { 00648 AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list); 00649 odbc_obj_disconnect(current); 00650 ast_mutex_destroy(¤t->lock); 00651 free(current); 00652 } 00653 AST_LIST_TRAVERSE_SAFE_END; 00654 00655 AST_LIST_REMOVE_CURRENT(&odbc_list, list); 00656 free(class); 00657 } 00658 } 00659 AST_LIST_TRAVERSE_SAFE_END; 00660 AST_LIST_UNLOCK(&odbc_list); 00661 00662 return 0; 00663 }
static int unload_module | ( | void | ) | [static] |
struct ast_cli_entry cli_odbc[] [static] |
Initial value:
{ { { "odbc", "show", NULL }, odbc_show_command, "List ODBC DSN(s)", show_usage }, }
Definition at line 347 of file res_odbc.c.
char show_usage[] [static] |
Initial value:
"Usage: odbc show [<class>]\n" " List settings of a particular ODBC class.\n" " or, if not specified, all classes.\n"
Definition at line 342 of file res_odbc.c.