Wed Aug 18 22:33:55 2010

Asterisk developer's documentation


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    <depend>ltdl</depend>
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;              /* Boolean - TDS databases need this */
00062    unsigned int delme:1;                /* Purge the class */
00063    unsigned int backslash_is_escape:1;  /* On this database, the backslash is a native escape sequence */
00064    unsigned int limit;                  /* 1023 wasn't enough for some people */
00065    unsigned int count;                  /* Running count of pooled connections */
00066    unsigned int idlecheck;              /* Recheck the connection if it is idle for this long */
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    /* Due to refcounts, we can safely assume that any objects with a reference
00082     * to us will prevent our destruction, so we don't need to worry about them.
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  * \brief Find or create an entry describing the table specified.
00123  * \param obj An active ODBC handle on which to query the table
00124  * \param table Tablename to describe
00125  * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
00126  * When a structure is returned, the contained columns list will be
00127  * rdlock'ed, to ensure that it will be retained in memory.
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    /* Table structure not already cached; build it now. */
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); /* SAFE */
00193       strcpy(tableptr->table, tablename); /* SAFE */
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          /* Specification states that the octenlen should be the maximum number of bytes
00215           * returned in a char or binary column, but it seems that some drivers just set
00216           * it to NULL. (Bad Postgres! No biscuit!) */
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          /* Insert column info into column list */
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       /* This prepare callback may do more than just prepare -- it may also
00303        * bind parameters, bind results, etc.  The real key, here, is that
00304        * when we disconnect, all handles become invalid for most databases.
00305        * We must therefore redo everything when we establish a new
00306        * connection. */
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              * While this isn't the best way to try to correct an error, this won't automatically
00331              * fail when the statement handle invalidates.
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) { /* Try to reconnect! */
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          /* Reset all to defaults for each class of odbc connections */
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                /* "shareconnections" is a little clearer in meaning than "pooling" */
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(&current->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(&current->lock);
00600                ao2_ref(current, -1);
00601             }
00602             ao2_iterator_destroy(&aoi2);
00603          } else {
00604             /* Should only ever be one of these */
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       /* I still have a reference in the caller, so a deref is NOT missing here. */
00631 
00632       if (preconnect) {
00633          /* Request and release builds a connection */
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    /* For pooled connections, this frees the connection to be
00649     * reused.  For non-pooled connections, it does nothing. */
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       /* Recycle connections before building another */
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          /* obj inherits the outstanding reference to class */
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          /* Object is not constructed, so delete outstanding reference to class. */
00726          ao2_ref(class, -1);
00727          class = NULL;
00728       }
00729    } else {
00730       /* Non-pooled connection: multiple modules can use the same connection. */
00731       aoi = ao2_iterator_init(class->obj_container, 0);
00732       while ((obj = ao2_iterator_next(&aoi))) {
00733          /* Non-pooled connection: if there is an entry, return it */
00734          break;
00735       }
00736 
00737       if (obj) {
00738          /* Object is not constructed, so delete outstanding reference to class. */
00739          ast_assert(ao2_ref(class, 0) > 1);
00740          ao2_ref(class, -1);
00741          class = NULL;
00742       } else {
00743          /* No entry: build one */
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          /* obj inherits the outstanding reference to class */
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    /* Nothing to disconnect */
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    /* First, mark all to be purged */
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    /* Purge remaining classes */
00892 
00893    /* Note on how this works; this is a case of circular references, so we
00894     * explicitly do NOT want to use a callback here (or we wind up in
00895     * recursive hell).
00896     *
00897     * 1. Iterate through all the classes.  Note that the classes will currently
00898     * contain two classes of the same name, one of which is marked delme and
00899     * will be purged when all remaining objects of the class are released, and
00900     * the other, which was created above when we re-parsed the config file.
00901     * 2. On each class, there is a reference held by the master container and
00902     * a reference held by each connection object.  There are two cases for
00903     * destruction of the class, noted below.  However, in all cases, all O-refs
00904     * (references to objects) will first be freed, which will cause the C-refs
00905     * (references to classes) to be decremented (but never to 0, because the
00906     * class container still has a reference).
00907     *    a) If the class has outstanding objects, the C-ref by the class
00908     *    container will then be freed, which leaves only C-refs by any
00909     *    outstanding objects.  When the final outstanding object is released
00910     *    (O-refs held by applications and dialplan functions), it will in turn
00911     *    free the final C-ref, causing class destruction.
00912     *    b) If the class has no outstanding objects, when the class container
00913     *    removes the final C-ref, the class will be destroyed.
00914     */
00915    aoi = ao2_iterator_init(class_container, 0);
00916    while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
00917       if (class->delme) {
00918          struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00919          while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
00920             ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
00921             ao2_ref(current, -1); /* O-ref-- (by iterator) */
00922             /* At this point, either
00923              * a) there's an outstanding O-ref, or
00924              * b) the object has already been destroyed.
00925              */
00926          }
00927          ao2_iterator_destroy(&aoi2);
00928          ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
00929          /* At this point, either
00930           * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
00931           * b) the last remaining C-ref is held by the iterator, which will be
00932           * destroyed in the next step.
00933           */
00934       }
00935       ao2_ref(class, -1); /* C-ref-- (by iterator) */
00936    }
00937    ao2_iterator_destroy(&aoi);
00938 
00939    /* Empty the cache; it will get rebuilt the next time the tables are needed. */
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    /* Prohibit unloading */
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           );

Generated on Wed Aug 18 22:33:55 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7