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: 176644 $")
00040
00041 #include "asterisk/file.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/lock.h"
00048 #include "asterisk/res_odbc.h"
00049 #include "asterisk/time.h"
00050 #include "asterisk/astobj2.h"
00051
00052 struct odbc_class
00053 {
00054 AST_LIST_ENTRY(odbc_class) list;
00055 char name[80];
00056 char dsn[80];
00057 char *username;
00058 char *password;
00059 char *sanitysql;
00060 SQLHENV env;
00061 unsigned int haspool:1;
00062 unsigned int delme:1;
00063 unsigned int backslash_is_escape:1;
00064 unsigned int limit;
00065 unsigned int count;
00066 unsigned int idlecheck;
00067 struct ao2_container *obj_container;
00068 };
00069
00070 struct ao2_container *class_container;
00071
00072 static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
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 static void odbc_class_destructor(void *data)
00079 {
00080 struct odbc_class *class = data;
00081
00082
00083
00084 if (class->username)
00085 ast_free(class->username);
00086 if (class->password)
00087 ast_free(class->password);
00088 if (class->sanitysql)
00089 ast_free(class->sanitysql);
00090 ao2_ref(class->obj_container, -1);
00091 SQLFreeHandle(SQL_HANDLE_ENV, class->env);
00092 }
00093
00094 static int null_hash_fn(const void *obj, const int flags)
00095 {
00096 return 0;
00097 }
00098
00099 static void odbc_obj_destructor(void *data)
00100 {
00101 struct odbc_obj *obj = data;
00102 struct odbc_class *class = obj->parent;
00103 obj->parent = NULL;
00104 odbc_obj_disconnect(obj);
00105 ast_mutex_destroy(&obj->lock);
00106 ao2_ref(class, -1);
00107 }
00108
00109 static void destroy_table_cache(struct odbc_cache_tables *table) {
00110 struct odbc_cache_columns *col;
00111 ast_debug(1, "Destroying table cache for %s\n", table->table);
00112 AST_RWLIST_WRLOCK(&table->columns);
00113 while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
00114 ast_free(col);
00115 }
00116 AST_RWLIST_UNLOCK(&table->columns);
00117 AST_RWLIST_HEAD_DESTROY(&table->columns);
00118 ast_free(table);
00119 }
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129 struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename)
00130 {
00131 struct odbc_cache_tables *tableptr;
00132 struct odbc_cache_columns *entry;
00133 char columnname[80];
00134 SQLLEN sqlptr;
00135 SQLHSTMT stmt = NULL;
00136 int res = 0, error = 0, try = 0;
00137 struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
00138
00139 AST_RWLIST_RDLOCK(&odbc_tables);
00140 AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
00141 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00142 break;
00143 }
00144 }
00145 if (tableptr) {
00146 AST_RWLIST_RDLOCK(&tableptr->columns);
00147 AST_RWLIST_UNLOCK(&odbc_tables);
00148 if (obj) {
00149 ast_odbc_release_obj(obj);
00150 }
00151 return tableptr;
00152 }
00153
00154 if (!obj) {
00155 ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
00156 return NULL;
00157 }
00158
00159
00160 do {
00161 retry:
00162 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00163 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00164 if (try == 0) {
00165 try = 1;
00166 ast_odbc_sanity_check(obj);
00167 goto retry;
00168 }
00169 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
00170 break;
00171 }
00172
00173 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00174 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00175 if (try == 0) {
00176 try = 1;
00177 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00178 ast_odbc_sanity_check(obj);
00179 goto retry;
00180 }
00181 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
00182 break;
00183 }
00184
00185 if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
00186 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
00187 break;
00188 }
00189
00190 tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00191 tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
00192 strcpy(tableptr->connection, database);
00193 strcpy(tableptr->table, tablename);
00194 AST_RWLIST_HEAD_INIT(&(tableptr->columns));
00195
00196 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00197 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00198
00199 if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
00200 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
00201 error = 1;
00202 break;
00203 }
00204 entry->name = (char *)entry + sizeof(*entry);
00205 strcpy(entry->name, columnname);
00206
00207 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00208 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00209 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00210 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00211 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00212 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00213
00214
00215
00216
00217 if (entry->octetlen == 0) {
00218 entry->octetlen = entry->size;
00219 }
00220
00221 ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
00222
00223 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00224 }
00225 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00226
00227 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00228 AST_RWLIST_RDLOCK(&(tableptr->columns));
00229 } while (0);
00230
00231 AST_RWLIST_UNLOCK(&odbc_tables);
00232
00233 if (error) {
00234 destroy_table_cache(tableptr);
00235 tableptr = NULL;
00236 }
00237 if (obj) {
00238 ast_odbc_release_obj(obj);
00239 }
00240 return tableptr;
00241 }
00242
00243 struct odbc_cache_columns *ast_odbc_find_column(struct odbc_cache_tables *table, const char *colname)
00244 {
00245 struct odbc_cache_columns *col;
00246 AST_RWLIST_TRAVERSE(&table->columns, col, list) {
00247 if (strcasecmp(col->name, colname) == 0) {
00248 return col;
00249 }
00250 }
00251 return NULL;
00252 }
00253
00254 int ast_odbc_clear_cache(const char *database, const char *tablename)
00255 {
00256 struct odbc_cache_tables *tableptr;
00257
00258 AST_RWLIST_WRLOCK(&odbc_tables);
00259 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
00260 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
00261 AST_LIST_REMOVE_CURRENT(list);
00262 destroy_table_cache(tableptr);
00263 break;
00264 }
00265 }
00266 AST_RWLIST_TRAVERSE_SAFE_END
00267 AST_RWLIST_UNLOCK(&odbc_tables);
00268 return tableptr ? 0 : -1;
00269 }
00270
00271 SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT (*exec_cb)(struct odbc_obj *obj, void *data), void *data)
00272 {
00273 int attempt;
00274 SQLHSTMT stmt;
00275
00276 for (attempt = 0; attempt < 2; attempt++) {
00277 stmt = exec_cb(obj, data);
00278
00279 if (stmt) {
00280 break;
00281 } else {
00282 obj->up = 0;
00283 ast_log(LOG_WARNING, "SQL Exec Direct failed. Attempting a reconnect...\n");
00284
00285 odbc_obj_disconnect(obj);
00286 odbc_obj_connect(obj);
00287 }
00288 }
00289
00290 return stmt;
00291 }
00292
00293 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
00294 {
00295 int res = 0, i, attempt;
00296 SQLINTEGER nativeerror=0, numfields=0;
00297 SQLSMALLINT diagbytes=0;
00298 unsigned char state[10], diagnostic[256];
00299 SQLHSTMT stmt;
00300
00301 for (attempt = 0; attempt < 2; attempt++) {
00302
00303
00304
00305
00306
00307 stmt = prepare_cb(obj, data);
00308
00309 if (stmt) {
00310 res = SQLExecute(stmt);
00311 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00312 if (res == SQL_ERROR) {
00313 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00314 for (i = 0; i < numfields; i++) {
00315 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00316 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00317 if (i > 10) {
00318 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00319 break;
00320 }
00321 }
00322 }
00323
00324 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00325 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00326 stmt = NULL;
00327
00328 obj->up = 0;
00329
00330
00331
00332
00333 ast_odbc_sanity_check(obj);
00334 continue;
00335 } else
00336 obj->last_used = ast_tvnow();
00337 break;
00338 } else if (attempt == 0)
00339 ast_odbc_sanity_check(obj);
00340 }
00341
00342 return stmt;
00343 }
00344
00345 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
00346 {
00347 int res = 0, i;
00348 SQLINTEGER nativeerror=0, numfields=0;
00349 SQLSMALLINT diagbytes=0;
00350 unsigned char state[10], diagnostic[256];
00351
00352 res = SQLExecute(stmt);
00353 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00354 if (res == SQL_ERROR) {
00355 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00356 for (i = 0; i < numfields; i++) {
00357 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00358 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00359 if (i > 10) {
00360 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00361 break;
00362 }
00363 }
00364 }
00365 } else
00366 obj->last_used = ast_tvnow();
00367
00368 return res;
00369 }
00370
00371
00372 int ast_odbc_sanity_check(struct odbc_obj *obj)
00373 {
00374 char *test_sql = "select 1";
00375 SQLHSTMT stmt;
00376 int res = 0;
00377
00378 if (!ast_strlen_zero(obj->parent->sanitysql))
00379 test_sql = obj->parent->sanitysql;
00380
00381 if (obj->up) {
00382 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00383 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00384 obj->up = 0;
00385 } else {
00386 res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00387 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00388 obj->up = 0;
00389 } else {
00390 res = SQLExecute(stmt);
00391 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00392 obj->up = 0;
00393 }
00394 }
00395 }
00396 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00397 }
00398
00399 if (!obj->up) {
00400 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00401 odbc_obj_disconnect(obj);
00402 odbc_obj_connect(obj);
00403 }
00404 return obj->up;
00405 }
00406
00407 static int load_odbc_config(void)
00408 {
00409 static char *cfg = "res_odbc.conf";
00410 struct ast_config *config;
00411 struct ast_variable *v;
00412 char *cat;
00413 const char *dsn, *username, *password, *sanitysql;
00414 int enabled, pooling, limit, bse;
00415 unsigned int idlecheck;
00416 int preconnect = 0, res = 0;
00417 struct ast_flags config_flags = { 0 };
00418
00419 struct odbc_class *new;
00420
00421 config = ast_config_load(cfg, config_flags);
00422 if (!config) {
00423 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
00424 return -1;
00425 }
00426 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
00427 if (!strcasecmp(cat, "ENV")) {
00428 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00429 setenv(v->name, v->value, 1);
00430 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
00431 }
00432 } else {
00433
00434 dsn = username = password = sanitysql = NULL;
00435 enabled = 1;
00436 preconnect = idlecheck = 0;
00437 pooling = 0;
00438 limit = 0;
00439 bse = 1;
00440 for (v = ast_variable_browse(config, cat); v; v = v->next) {
00441 if (!strcasecmp(v->name, "pooling")) {
00442 if (ast_true(v->value))
00443 pooling = 1;
00444 } else if (!strncasecmp(v->name, "share", 5)) {
00445
00446 if (ast_false(v->value))
00447 pooling = 1;
00448 } else if (!strcasecmp(v->name, "limit")) {
00449 sscanf(v->value, "%d", &limit);
00450 if (ast_true(v->value) && !limit) {
00451 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);
00452 limit = 1023;
00453 } else if (ast_false(v->value)) {
00454 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'. Disabling ODBC class '%s'.\n", v->value, cat);
00455 enabled = 0;
00456 break;
00457 }
00458 } else if (!strcasecmp(v->name, "idlecheck")) {
00459 sscanf(v->value, "%d", &idlecheck);
00460 } else if (!strcasecmp(v->name, "enabled")) {
00461 enabled = ast_true(v->value);
00462 } else if (!strcasecmp(v->name, "pre-connect")) {
00463 preconnect = ast_true(v->value);
00464 } else if (!strcasecmp(v->name, "dsn")) {
00465 dsn = v->value;
00466 } else if (!strcasecmp(v->name, "username")) {
00467 username = v->value;
00468 } else if (!strcasecmp(v->name, "password")) {
00469 password = v->value;
00470 } else if (!strcasecmp(v->name, "sanitysql")) {
00471 sanitysql = v->value;
00472 } else if (!strcasecmp(v->name, "backslash_is_escape")) {
00473 bse = ast_true(v->value);
00474 }
00475 }
00476
00477 if (enabled && !ast_strlen_zero(dsn)) {
00478 new = ao2_alloc(sizeof(*new), odbc_class_destructor);
00479
00480 if (!new) {
00481 res = -1;
00482 break;
00483 }
00484
00485 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
00486 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
00487
00488 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00489 ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
00490 ao2_ref(new, -1);
00491 return res;
00492 }
00493
00494 new->obj_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr);
00495
00496 if (pooling) {
00497 new->haspool = pooling;
00498 if (limit) {
00499 new->limit = limit;
00500 } else {
00501 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless. Changing limit from 0 to 5.\n");
00502 new->limit = 5;
00503 }
00504 }
00505
00506 new->backslash_is_escape = bse ? 1 : 0;
00507 new->idlecheck = idlecheck;
00508
00509 if (cat)
00510 ast_copy_string(new->name, cat, sizeof(new->name));
00511 if (dsn)
00512 ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
00513 if (username && !(new->username = ast_strdup(username))) {
00514 ao2_ref(new, -1);
00515 break;
00516 }
00517 if (password && !(new->password = ast_strdup(password))) {
00518 ao2_ref(new, -1);
00519 break;
00520 }
00521 if (sanitysql && !(new->sanitysql = ast_strdup(sanitysql))) {
00522 ao2_ref(new, -1);
00523 break;
00524 }
00525
00526 odbc_register_class(new, preconnect);
00527 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
00528 ao2_ref(new, -1);
00529 new = NULL;
00530 }
00531 }
00532 }
00533 ast_config_destroy(config);
00534 return res;
00535 }
00536
00537 static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00538 {
00539 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00540 struct odbc_class *class;
00541 struct odbc_obj *current;
00542 int length = 0;
00543 int which = 0;
00544 char *ret = NULL;
00545
00546 switch (cmd) {
00547 case CLI_INIT:
00548 e->command = "odbc show";
00549 e->usage =
00550 "Usage: odbc show [class]\n"
00551 " List settings of a particular ODBC class or,\n"
00552 " if not specified, all classes.\n";
00553 return NULL;
00554 case CLI_GENERATE:
00555 if (a->pos != 2)
00556 return NULL;
00557 length = strlen(a->word);
00558 while ((class = ao2_iterator_next(&aoi))) {
00559 if (!strncasecmp(a->word, class->name, length) && ++which > a->n) {
00560 ret = ast_strdup(class->name);
00561 }
00562 ao2_ref(class, -1);
00563 if (ret) {
00564 break;
00565 }
00566 }
00567 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00568 ret = ast_strdup("all");
00569 }
00570 return ret;
00571 }
00572
00573 ast_cli(a->fd, "\nODBC DSN Settings\n");
00574 ast_cli(a->fd, "-----------------\n\n");
00575 aoi = ao2_iterator_init(class_container, 0);
00576 while ((class = ao2_iterator_next(&aoi))) {
00577 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00578 int count = 0;
00579 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
00580
00581 if (class->haspool) {
00582 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00583
00584 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count);
00585
00586 while ((current = ao2_iterator_next(&aoi2))) {
00587 ast_mutex_lock(¤t->lock);
00588 #ifdef DEBUG_THREADS
00589 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count,
00590 current->used ? "in use" :
00591 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00592 current->file, current->lineno, current->function);
00593 #else
00594 ast_cli(a->fd, " - Connection %d: %s\n", ++count,
00595 current->used ? "in use" :
00596 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00597 #endif
00598 ast_mutex_unlock(¤t->lock);
00599 ao2_ref(current, -1);
00600 }
00601 } else {
00602
00603 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00604 while ((current = ao2_iterator_next(&aoi2))) {
00605 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
00606 ao2_ref(current, -1);
00607 }
00608 }
00609 ast_cli(a->fd, "\n");
00610 }
00611 ao2_ref(class, -1);
00612 }
00613
00614 return CLI_SUCCESS;
00615 }
00616
00617 static struct ast_cli_entry cli_odbc[] = {
00618 AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
00619 };
00620
00621 static int odbc_register_class(struct odbc_class *class, int preconnect)
00622 {
00623 struct odbc_obj *obj;
00624 if (class) {
00625 ao2_link(class_container, class);
00626
00627
00628 if (preconnect) {
00629
00630 obj = ast_odbc_request_obj(class->name, 0);
00631 if (obj)
00632 ast_odbc_release_obj(obj);
00633 }
00634
00635 return 0;
00636 } else {
00637 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00638 return -1;
00639 }
00640 }
00641
00642 void ast_odbc_release_obj(struct odbc_obj *obj)
00643 {
00644
00645
00646 obj->used = 0;
00647 #ifdef DEBUG_THREADS
00648 obj->file[0] = '\0';
00649 obj->function[0] = '\0';
00650 obj->lineno = 0;
00651 #endif
00652 ao2_ref(obj, -1);
00653 }
00654
00655 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
00656 {
00657 return obj->parent->backslash_is_escape;
00658 }
00659
00660 #ifdef DEBUG_THREADS
00661 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
00662 #else
00663 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
00664 #endif
00665 {
00666 struct odbc_obj *obj = NULL;
00667 struct odbc_class *class;
00668 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00669
00670 while ((class = ao2_iterator_next(&aoi))) {
00671 if (!strcmp(class->name, name) && !class->delme) {
00672 break;
00673 }
00674 ao2_ref(class, -1);
00675 }
00676
00677 if (!class)
00678 return NULL;
00679
00680 ast_assert(ao2_ref(class, 0) > 1);
00681
00682 if (class->haspool) {
00683
00684 aoi = ao2_iterator_init(class->obj_container, 0);
00685 while ((obj = ao2_iterator_next(&aoi))) {
00686 if (! obj->used) {
00687 ast_mutex_lock(&obj->lock);
00688 obj->used = 1;
00689 ast_mutex_unlock(&obj->lock);
00690 break;
00691 }
00692 ao2_ref(obj, -1);
00693 }
00694
00695 if (obj) {
00696 ast_assert(ao2_ref(obj, 0) > 1);
00697 }
00698
00699 if (!obj && (class->count < class->limit)) {
00700 class->count++;
00701 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
00702 if (!obj) {
00703 ao2_ref(class, -1);
00704 return NULL;
00705 }
00706 ast_assert(ao2_ref(obj, 0) == 1);
00707 ast_mutex_init(&obj->lock);
00708
00709 obj->parent = class;
00710 if (odbc_obj_connect(obj) == ODBC_FAIL) {
00711 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00712 ao2_ref(obj, -1);
00713 ast_assert(ao2_ref(class, 0) > 0);
00714 obj = NULL;
00715 } else {
00716 obj->used = 1;
00717 ao2_link(class->obj_container, obj);
00718 }
00719 class = NULL;
00720 } else {
00721
00722 ao2_ref(class, -1);
00723 class = NULL;
00724 }
00725 } else {
00726
00727 aoi = ao2_iterator_init(class->obj_container, 0);
00728 while ((obj = ao2_iterator_next(&aoi))) {
00729
00730 break;
00731 }
00732
00733 if (obj) {
00734
00735 ast_assert(ao2_ref(class, 0) > 1);
00736 ao2_ref(class, -1);
00737 class = NULL;
00738 } else {
00739
00740 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
00741 if (!obj) {
00742 ast_assert(ao2_ref(class, 0) > 1);
00743 ao2_ref(class, -1);
00744 return NULL;
00745 }
00746 ast_mutex_init(&obj->lock);
00747
00748 obj->parent = class;
00749 if (odbc_obj_connect(obj) == ODBC_FAIL) {
00750 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00751 ao2_ref(obj, -1);
00752 obj = NULL;
00753 } else {
00754 ao2_link(class->obj_container, obj);
00755 ast_assert(ao2_ref(obj, 0) > 1);
00756 }
00757 class = NULL;
00758 }
00759 }
00760
00761 if (obj && check) {
00762 ast_odbc_sanity_check(obj);
00763 } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
00764 odbc_obj_connect(obj);
00765
00766 #ifdef DEBUG_THREADS
00767 if (obj) {
00768 ast_copy_string(obj->file, file, sizeof(obj->file));
00769 ast_copy_string(obj->function, function, sizeof(obj->function));
00770 obj->lineno = lineno;
00771 }
00772 #endif
00773 ast_assert(class == NULL);
00774
00775 if (obj) {
00776 ast_assert(ao2_ref(obj, 0) > 1);
00777 }
00778 return obj;
00779 }
00780
00781 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
00782 {
00783 int res;
00784 SQLINTEGER err;
00785 short int mlen;
00786 unsigned char msg[200], stat[10];
00787
00788
00789 if (!obj->con) {
00790 return ODBC_SUCCESS;
00791 }
00792
00793 ast_mutex_lock(&obj->lock);
00794
00795 res = SQLDisconnect(obj->con);
00796
00797 if (obj->parent) {
00798 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
00799 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00800 } else {
00801 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00802 }
00803 }
00804
00805 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
00806 obj->con = NULL;
00807 ast_log(LOG_DEBUG, "Database handle deallocated\n");
00808 } else {
00809 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00810 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00811 }
00812
00813 obj->up = 0;
00814 ast_mutex_unlock(&obj->lock);
00815 return ODBC_SUCCESS;
00816 }
00817
00818 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
00819 {
00820 int res;
00821 SQLINTEGER err;
00822 short int mlen;
00823 unsigned char msg[200], state[10];
00824 #ifdef NEEDTRACE
00825 SQLINTEGER enable = 1;
00826 char *tracefile = "/tmp/odbc.trace";
00827 #endif
00828 ast_mutex_lock(&obj->lock);
00829
00830 if (obj->up) {
00831 odbc_obj_disconnect(obj);
00832 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00833 } else {
00834 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00835 }
00836
00837 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00838
00839 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00840 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00841 ast_mutex_unlock(&obj->lock);
00842 return ODBC_FAIL;
00843 }
00844 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00845 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00846 #ifdef NEEDTRACE
00847 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00848 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00849 #endif
00850
00851 res = SQLConnect(obj->con,
00852 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00853 (SQLCHAR *) obj->parent->username, SQL_NTS,
00854 (SQLCHAR *) obj->parent->password, SQL_NTS);
00855
00856 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00857 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
00858 ast_mutex_unlock(&obj->lock);
00859 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00860 return ODBC_FAIL;
00861 } else {
00862 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00863 obj->up = 1;
00864 obj->last_used = ast_tvnow();
00865 }
00866
00867 ast_mutex_unlock(&obj->lock);
00868 return ODBC_SUCCESS;
00869 }
00870
00871 static int reload(void)
00872 {
00873 struct odbc_cache_tables *table;
00874 struct odbc_class *class;
00875 struct odbc_obj *current;
00876 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00877
00878
00879 while ((class = ao2_iterator_next(&aoi))) {
00880 class->delme = 1;
00881 ao2_ref(class, -1);
00882 }
00883
00884 load_odbc_config();
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910 aoi = ao2_iterator_init(class_container, 0);
00911 while ((class = ao2_iterator_next(&aoi))) {
00912 if (class->delme) {
00913 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00914 while ((current = ao2_iterator_next(&aoi2))) {
00915 ao2_unlink(class->obj_container, current);
00916 ao2_ref(current, -1);
00917
00918
00919
00920
00921 }
00922 ao2_unlink(class_container, class);
00923
00924
00925
00926
00927
00928 }
00929 ao2_ref(class, -1);
00930 }
00931
00932
00933 AST_RWLIST_WRLOCK(&odbc_tables);
00934 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00935 destroy_table_cache(table);
00936 }
00937 AST_RWLIST_UNLOCK(&odbc_tables);
00938
00939 return 0;
00940 }
00941
00942 static int unload_module(void)
00943 {
00944
00945 return -1;
00946 }
00947
00948 static int load_module(void)
00949 {
00950 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
00951 return AST_MODULE_LOAD_DECLINE;
00952 if (load_odbc_config() == -1)
00953 return AST_MODULE_LOAD_DECLINE;
00954 ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00955 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00956 return 0;
00957 }
00958
00959 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
00960 .load = load_module,
00961 .unload = unload_module,
00962 .reload = reload,
00963 );