Fri Jul 24 00:41:02 2009

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: 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;              /* 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, "%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(&current->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(&current->lock);
00599                ao2_ref(current, -1);
00600             }
00601          } else {
00602             /* Should only ever be one of these */
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       /* I still have a reference in the caller, so a deref is NOT missing here. */
00627 
00628       if (preconnect) {
00629          /* Request and release builds a connection */
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    /* For pooled connections, this frees the connection to be
00645     * reused.  For non-pooled connections, it does nothing. */
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       /* Recycle connections before building another */
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          /* obj inherits the outstanding reference to class */
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          /* Object is not constructed, so delete outstanding reference to class. */
00722          ao2_ref(class, -1);
00723          class = NULL;
00724       }
00725    } else {
00726       /* Non-pooled connection: multiple modules can use the same connection. */
00727       aoi = ao2_iterator_init(class->obj_container, 0);
00728       while ((obj = ao2_iterator_next(&aoi))) {
00729          /* Non-pooled connection: if there is an entry, return it */
00730          break;
00731       }
00732 
00733       if (obj) {
00734          /* Object is not constructed, so delete outstanding reference to class. */
00735          ast_assert(ao2_ref(class, 0) > 1);
00736          ao2_ref(class, -1);
00737          class = NULL;
00738       } else {
00739          /* No entry: build one */
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          /* obj inherits the outstanding reference to class */
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    /* Nothing to disconnect */
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    /* First, mark all to be purged */
00879    while ((class = ao2_iterator_next(&aoi))) {
00880       class->delme = 1;
00881       ao2_ref(class, -1);
00882    }
00883 
00884    load_odbc_config();
00885 
00886    /* Purge remaining classes */
00887 
00888    /* Note on how this works; this is a case of circular references, so we
00889     * explicitly do NOT want to use a callback here (or we wind up in
00890     * recursive hell).
00891     *
00892     * 1. Iterate through all the classes.  Note that the classes will currently
00893     * contain two classes of the same name, one of which is marked delme and
00894     * will be purged when all remaining objects of the class are released, and
00895     * the other, which was created above when we re-parsed the config file.
00896     * 2. On each class, there is a reference held by the master container and
00897     * a reference held by each connection object.  There are two cases for
00898     * destruction of the class, noted below.  However, in all cases, all O-refs
00899     * (references to objects) will first be freed, which will cause the C-refs
00900     * (references to classes) to be decremented (but never to 0, because the
00901     * class container still has a reference).
00902     *    a) If the class has outstanding objects, the C-ref by the class
00903     *    container will then be freed, which leaves only C-refs by any
00904     *    outstanding objects.  When the final outstanding object is released
00905     *    (O-refs held by applications and dialplan functions), it will in turn
00906     *    free the final C-ref, causing class destruction.
00907     *    b) If the class has no outstanding objects, when the class container
00908     *    removes the final C-ref, the class will be destroyed.
00909     */
00910    aoi = ao2_iterator_init(class_container, 0);
00911    while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
00912       if (class->delme) {
00913          struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
00914          while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
00915             ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
00916             ao2_ref(current, -1); /* O-ref-- (by iterator) */
00917             /* At this point, either
00918              * a) there's an outstanding O-ref, or
00919              * b) the object has already been destroyed.
00920              */
00921          }
00922          ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
00923          /* At this point, either
00924           * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
00925           * b) the last remaining C-ref is held by the iterator, which will be
00926           * destroyed in the next step.
00927           */
00928       }
00929       ao2_ref(class, -1); /* C-ref-- (by iterator) */
00930    }
00931 
00932    /* Empty the cache; it will get rebuilt the next time the tables are needed. */
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    /* Prohibit unloading */
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           );

Generated on Fri Jul 24 00:41:02 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7