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: 222186 $")
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, "%30d", &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, "%30u", &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 ao2_iterator_destroy(&aoi);
00568 if (!ret && !strncasecmp(a->word, "all", length) && ++which > a->n) {
00569 ret = ast_strdup("all");
00570 }
00571 return ret;
00572 }
00573
00574 ast_cli(a->fd, "\nODBC DSN Settings\n");
00575 ast_cli(a->fd, "-----------------\n\n");
00576 aoi = ao2_iterator_init(class_container, 0);
00577 while ((class = ao2_iterator_next(&aoi))) {
00578 if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
00579 int count = 0;
00580 ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
00581
00582 if (class->haspool) {
00583 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00584
00585 ast_cli(a->fd, " Pooled: Yes\n Limit: %d\n Connections in use: %d\n", class->limit, class->count);
00586
00587 while ((current = ao2_iterator_next(&aoi2))) {
00588 ast_mutex_lock(¤t->lock);
00589 #ifdef DEBUG_THREADS
00590 ast_cli(a->fd, " - Connection %d: %s (%s:%d %s)\n", ++count,
00591 current->used ? "in use" :
00592 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected",
00593 current->file, current->lineno, current->function);
00594 #else
00595 ast_cli(a->fd, " - Connection %d: %s\n", ++count,
00596 current->used ? "in use" :
00597 current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
00598 #endif
00599 ast_mutex_unlock(¤t->lock);
00600 ao2_ref(current, -1);
00601 }
00602 ao2_iterator_destroy(&aoi2);
00603 } else {
00604
00605 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00606 while ((current = ao2_iterator_next(&aoi2))) {
00607 ast_cli(a->fd, " Pooled: No\n Connected: %s\n", current->up && ast_odbc_sanity_check(current) ? "Yes" : "No");
00608 ao2_ref(current, -1);
00609 }
00610 ao2_iterator_destroy(&aoi2);
00611 }
00612 ast_cli(a->fd, "\n");
00613 }
00614 ao2_ref(class, -1);
00615 }
00616 ao2_iterator_destroy(&aoi);
00617
00618 return CLI_SUCCESS;
00619 }
00620
00621 static struct ast_cli_entry cli_odbc[] = {
00622 AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
00623 };
00624
00625 static int odbc_register_class(struct odbc_class *class, int preconnect)
00626 {
00627 struct odbc_obj *obj;
00628 if (class) {
00629 ao2_link(class_container, class);
00630
00631
00632 if (preconnect) {
00633
00634 obj = ast_odbc_request_obj(class->name, 0);
00635 if (obj)
00636 ast_odbc_release_obj(obj);
00637 }
00638
00639 return 0;
00640 } else {
00641 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
00642 return -1;
00643 }
00644 }
00645
00646 void ast_odbc_release_obj(struct odbc_obj *obj)
00647 {
00648
00649
00650 obj->used = 0;
00651 #ifdef DEBUG_THREADS
00652 obj->file[0] = '\0';
00653 obj->function[0] = '\0';
00654 obj->lineno = 0;
00655 #endif
00656 ao2_ref(obj, -1);
00657 }
00658
00659 int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
00660 {
00661 return obj->parent->backslash_is_escape;
00662 }
00663
00664 #ifdef DEBUG_THREADS
00665 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno)
00666 #else
00667 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
00668 #endif
00669 {
00670 struct odbc_obj *obj = NULL;
00671 struct odbc_class *class;
00672 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00673
00674 while ((class = ao2_iterator_next(&aoi))) {
00675 if (!strcmp(class->name, name) && !class->delme) {
00676 break;
00677 }
00678 ao2_ref(class, -1);
00679 }
00680
00681 if (!class)
00682 return NULL;
00683
00684 ast_assert(ao2_ref(class, 0) > 1);
00685
00686 if (class->haspool) {
00687
00688 aoi = ao2_iterator_init(class->obj_container, 0);
00689 while ((obj = ao2_iterator_next(&aoi))) {
00690 if (! obj->used) {
00691 ast_mutex_lock(&obj->lock);
00692 obj->used = 1;
00693 ast_mutex_unlock(&obj->lock);
00694 break;
00695 }
00696 ao2_ref(obj, -1);
00697 }
00698
00699 if (obj) {
00700 ast_assert(ao2_ref(obj, 0) > 1);
00701 }
00702
00703 if (!obj && (class->count < class->limit)) {
00704 class->count++;
00705 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
00706 if (!obj) {
00707 ao2_ref(class, -1);
00708 return NULL;
00709 }
00710 ast_assert(ao2_ref(obj, 0) == 1);
00711 ast_mutex_init(&obj->lock);
00712
00713 obj->parent = class;
00714 if (odbc_obj_connect(obj) == ODBC_FAIL) {
00715 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00716 ao2_ref(obj, -1);
00717 ast_assert(ao2_ref(class, 0) > 0);
00718 obj = NULL;
00719 } else {
00720 obj->used = 1;
00721 ao2_link(class->obj_container, obj);
00722 }
00723 class = NULL;
00724 } else {
00725
00726 ao2_ref(class, -1);
00727 class = NULL;
00728 }
00729 } else {
00730
00731 aoi = ao2_iterator_init(class->obj_container, 0);
00732 while ((obj = ao2_iterator_next(&aoi))) {
00733
00734 break;
00735 }
00736
00737 if (obj) {
00738
00739 ast_assert(ao2_ref(class, 0) > 1);
00740 ao2_ref(class, -1);
00741 class = NULL;
00742 } else {
00743
00744 obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
00745 if (!obj) {
00746 ast_assert(ao2_ref(class, 0) > 1);
00747 ao2_ref(class, -1);
00748 return NULL;
00749 }
00750 ast_mutex_init(&obj->lock);
00751
00752 obj->parent = class;
00753 if (odbc_obj_connect(obj) == ODBC_FAIL) {
00754 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00755 ao2_ref(obj, -1);
00756 obj = NULL;
00757 } else {
00758 ao2_link(class->obj_container, obj);
00759 ast_assert(ao2_ref(obj, 0) > 1);
00760 }
00761 class = NULL;
00762 }
00763 }
00764
00765 if (obj && check) {
00766 ast_odbc_sanity_check(obj);
00767 } else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
00768 odbc_obj_connect(obj);
00769
00770 #ifdef DEBUG_THREADS
00771 if (obj) {
00772 ast_copy_string(obj->file, file, sizeof(obj->file));
00773 ast_copy_string(obj->function, function, sizeof(obj->function));
00774 obj->lineno = lineno;
00775 }
00776 #endif
00777 ast_assert(class == NULL);
00778
00779 if (obj) {
00780 ast_assert(ao2_ref(obj, 0) > 1);
00781 }
00782 return obj;
00783 }
00784
00785 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
00786 {
00787 int res;
00788 SQLINTEGER err;
00789 short int mlen;
00790 unsigned char msg[200], stat[10];
00791
00792
00793 if (!obj->con) {
00794 return ODBC_SUCCESS;
00795 }
00796
00797 ast_mutex_lock(&obj->lock);
00798
00799 res = SQLDisconnect(obj->con);
00800
00801 if (obj->parent) {
00802 if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
00803 ast_log(LOG_DEBUG, "Disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
00804 } else {
00805 ast_log(LOG_DEBUG, "res_odbc: %s [%s] already disconnected\n", obj->parent->name, obj->parent->dsn);
00806 }
00807 }
00808
00809 if ((res = SQLFreeHandle(SQL_HANDLE_DBC, obj->con) == SQL_SUCCESS)) {
00810 obj->con = NULL;
00811 ast_log(LOG_DEBUG, "Database handle deallocated\n");
00812 } else {
00813 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
00814 ast_log(LOG_WARNING, "Unable to deallocate database handle? %d errno=%d %s\n", res, (int)err, msg);
00815 }
00816
00817 obj->up = 0;
00818 ast_mutex_unlock(&obj->lock);
00819 return ODBC_SUCCESS;
00820 }
00821
00822 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
00823 {
00824 int res;
00825 SQLINTEGER err;
00826 short int mlen;
00827 unsigned char msg[200], state[10];
00828 #ifdef NEEDTRACE
00829 SQLINTEGER enable = 1;
00830 char *tracefile = "/tmp/odbc.trace";
00831 #endif
00832 ast_mutex_lock(&obj->lock);
00833
00834 if (obj->up) {
00835 odbc_obj_disconnect(obj);
00836 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
00837 } else {
00838 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
00839 }
00840
00841 res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
00842
00843 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00844 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
00845 ast_mutex_unlock(&obj->lock);
00846 return ODBC_FAIL;
00847 }
00848 SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
00849 SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
00850 #ifdef NEEDTRACE
00851 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00852 SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00853 #endif
00854
00855 res = SQLConnect(obj->con,
00856 (SQLCHAR *) obj->parent->dsn, SQL_NTS,
00857 (SQLCHAR *) obj->parent->username, SQL_NTS,
00858 (SQLCHAR *) obj->parent->password, SQL_NTS);
00859
00860 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00861 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
00862 ast_mutex_unlock(&obj->lock);
00863 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
00864 return ODBC_FAIL;
00865 } else {
00866 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
00867 obj->up = 1;
00868 obj->last_used = ast_tvnow();
00869 }
00870
00871 ast_mutex_unlock(&obj->lock);
00872 return ODBC_SUCCESS;
00873 }
00874
00875 static int reload(void)
00876 {
00877 struct odbc_cache_tables *table;
00878 struct odbc_class *class;
00879 struct odbc_obj *current;
00880 struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
00881
00882
00883 while ((class = ao2_iterator_next(&aoi))) {
00884 class->delme = 1;
00885 ao2_ref(class, -1);
00886 }
00887 ao2_iterator_destroy(&aoi);
00888
00889 load_odbc_config();
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915 aoi = ao2_iterator_init(class_container, 0);
00916 while ((class = ao2_iterator_next(&aoi))) {
00917 if (class->delme) {
00918 struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00919 while ((current = ao2_iterator_next(&aoi2))) {
00920 ao2_unlink(class->obj_container, current);
00921 ao2_ref(current, -1);
00922
00923
00924
00925
00926 }
00927 ao2_iterator_destroy(&aoi2);
00928 ao2_unlink(class_container, class);
00929
00930
00931
00932
00933
00934 }
00935 ao2_ref(class, -1);
00936 }
00937 ao2_iterator_destroy(&aoi);
00938
00939
00940 AST_RWLIST_WRLOCK(&odbc_tables);
00941 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00942 destroy_table_cache(table);
00943 }
00944 AST_RWLIST_UNLOCK(&odbc_tables);
00945
00946 return 0;
00947 }
00948
00949 static int unload_module(void)
00950 {
00951
00952 return -1;
00953 }
00954
00955 static int load_module(void)
00956 {
00957 if (!(class_container = ao2_container_alloc(1, null_hash_fn, ao2_match_by_addr)))
00958 return AST_MODULE_LOAD_DECLINE;
00959 if (load_odbc_config() == -1)
00960 return AST_MODULE_LOAD_DECLINE;
00961 ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
00962 ast_log(LOG_NOTICE, "res_odbc loaded.\n");
00963 return 0;
00964 }
00965
00966 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
00967 .load = load_module,
00968 .unload = unload_module,
00969 .reload = reload,
00970 );