Mon May 14 04:42:58 2007

Asterisk developer's documentation


func_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (c) 2005, 2006 Tilghman Lesher
00005  *
00006  * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  *
00022  * \brief ODBC lookups
00023  *
00024  * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
00025  *
00026  * \ingroup functions
00027  */
00028 
00029 /*** MODULEINFO
00030    <depend>unixodbc</depend>
00031  ***/
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00036 
00037 #include <sys/types.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <string.h>
00042 #include <errno.h>
00043 
00044 #include "asterisk/module.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/logger.h"
00047 #include "asterisk/options.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/res_odbc.h"
00053 #include "asterisk/app.h"
00054 
00055 static char *config = "func_odbc.conf";
00056 
00057 enum {
00058    OPT_ESCAPECOMMAS =   (1 << 0),
00059 } odbc_option_flags;
00060 
00061 struct acf_odbc_query {
00062    AST_LIST_ENTRY(acf_odbc_query) list;
00063    char readhandle[5][30];
00064    char writehandle[5][30];
00065    char sql_read[2048];
00066    char sql_write[2048];
00067    unsigned int flags;
00068    struct ast_custom_function *acf;
00069 };
00070 
00071 AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
00072 
00073 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
00074 {
00075    int res;
00076    char *sql = data;
00077    SQLHSTMT stmt;
00078 
00079    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00080    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00081       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00082       return NULL;
00083    }
00084 
00085    res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
00086    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00087       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00088       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00089       return NULL;
00090    }
00091 
00092    return stmt;
00093 }
00094 
00095 /*
00096  * Master control routine
00097  */
00098 static int acf_odbc_write(struct ast_channel *chan, char *cmd, char *s, const char *value)
00099 {
00100    struct odbc_obj *obj;
00101    struct acf_odbc_query *query;
00102    char *t, buf[2048]="", varname[15];
00103    int i, dsn;
00104    AST_DECLARE_APP_ARGS(values,
00105       AST_APP_ARG(field)[100];
00106    );
00107    AST_DECLARE_APP_ARGS(args,
00108       AST_APP_ARG(field)[100];
00109    );
00110    SQLHSTMT stmt;
00111    SQLLEN rows=0;
00112 
00113    AST_LIST_LOCK(&queries);
00114    AST_LIST_TRAVERSE(&queries, query, list) {
00115       if (!strcmp(query->acf->name, cmd)) {
00116          break;
00117       }
00118    }
00119 
00120    if (!query) {
00121       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00122       AST_LIST_UNLOCK(&queries);
00123       return -1;
00124    }
00125 
00126    /* Parse our arguments */
00127    t = value ? ast_strdupa(value) : "";
00128 
00129    if (!s || !t) {
00130       ast_log(LOG_ERROR, "Out of memory\n");
00131       AST_LIST_UNLOCK(&queries);
00132       return -1;
00133    }
00134 
00135    AST_STANDARD_APP_ARGS(args, s);
00136    for (i = 0; i < args.argc; i++) {
00137       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00138       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00139    }
00140 
00141    /* Parse values, just like arguments */
00142    /* Can't use the pipe, because app Set removes them */
00143    AST_NONSTANDARD_APP_ARGS(values, t, ',');
00144    for (i = 0; i < values.argc; i++) {
00145       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00146       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00147    }
00148 
00149    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
00150    pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00151 
00152    pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
00153 
00154    /* Restore prior values */
00155    for (i = 0; i < args.argc; i++) {
00156       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00157       pbx_builtin_setvar_helper(chan, varname, NULL);
00158    }
00159 
00160    for (i = 0; i < values.argc; i++) {
00161       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00162       pbx_builtin_setvar_helper(chan, varname, NULL);
00163    }
00164    pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00165 
00166    AST_LIST_UNLOCK(&queries);
00167 
00168    for (dsn = 0; dsn < 5; dsn++) {
00169       if (!ast_strlen_zero(query->writehandle[dsn])) {
00170          obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00171          if (obj)
00172             stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, buf);
00173       }
00174       if (stmt)
00175          break;
00176    }
00177 
00178    if (stmt) {
00179       /* Rows affected */
00180       SQLRowCount(stmt, &rows);
00181    }
00182 
00183    /* Output the affected rows, for all cases.  In the event of failure, we
00184     * flag this as -1 rows.  Note that this is different from 0 affected rows
00185     * which would be the case if we succeeded in our query, but the values did
00186     * not change. */
00187    snprintf(varname, sizeof(varname), "%d", (int)rows);
00188    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00189 
00190    if (stmt)
00191       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00192    if (obj)
00193       ast_odbc_release_obj(obj);
00194 
00195    return 0;
00196 }
00197 
00198 static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf, size_t len)
00199 {
00200    struct odbc_obj *obj;
00201    struct acf_odbc_query *query;
00202    char sql[2048] = "", varname[15], colnames[2048] = "";
00203    int res, x, buflen = 0, escapecommas, dsn;
00204    AST_DECLARE_APP_ARGS(args,
00205       AST_APP_ARG(field)[100];
00206    );
00207    SQLHSTMT stmt;
00208    SQLSMALLINT colcount=0;
00209    SQLLEN indicator;
00210    SQLSMALLINT collength;
00211 
00212    AST_LIST_LOCK(&queries);
00213    AST_LIST_TRAVERSE(&queries, query, list) {
00214       if (!strcmp(query->acf->name, cmd)) {
00215          break;
00216       }
00217    }
00218 
00219    if (!query) {
00220       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00221       AST_LIST_UNLOCK(&queries);
00222       return -1;
00223    }
00224 
00225    AST_STANDARD_APP_ARGS(args, s);
00226    for (x = 0; x < args.argc; x++) {
00227       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00228       pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00229    }
00230 
00231    pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
00232 
00233    /* Restore prior values */
00234    for (x = 0; x < args.argc; x++) {
00235       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00236       pbx_builtin_setvar_helper(chan, varname, NULL);
00237    }
00238 
00239    /* Save this flag, so we can release the lock */
00240    escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00241 
00242    AST_LIST_UNLOCK(&queries);
00243 
00244    for (dsn = 0; dsn < 5; dsn++) {
00245       if (!ast_strlen_zero(query->writehandle[dsn])) {
00246          obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00247          if (obj)
00248             stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql);
00249       }
00250       if (stmt)
00251          break;
00252    }
00253 
00254    if (!stmt) {
00255       ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql);
00256       if (obj)
00257          ast_odbc_release_obj(obj);
00258       return -1;
00259    }
00260 
00261    res = SQLNumResultCols(stmt, &colcount);
00262    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00263       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00264       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00265       ast_odbc_release_obj(obj);
00266       return -1;
00267    }
00268 
00269    *buf = '\0';
00270 
00271    res = SQLFetch(stmt);
00272    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00273       int res1 = -1;
00274       if (res == SQL_NO_DATA) {
00275          if (option_verbose > 3) {
00276             ast_verbose(VERBOSE_PREFIX_4 "Found no rows [%s]\n", sql);
00277          }
00278          res1 = 0;
00279       } else if (option_verbose > 3) {
00280          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
00281       }
00282       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00283       ast_odbc_release_obj(obj);
00284       return res1;
00285    }
00286 
00287    for (x = 0; x < colcount; x++) {
00288       int i, namelen;
00289       char coldata[256], colname[256];
00290 
00291       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
00292       if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00293          snprintf(colname, sizeof(colname), "field%d", x);
00294       }
00295 
00296       if (!ast_strlen_zero(colnames))
00297          strncat(colnames, ",", sizeof(colnames) - 1);
00298       namelen = strlen(colnames);
00299 
00300       /* Copy data, encoding '\' and ',' for the argument parser */
00301       for (i = 0; i < sizeof(colname); i++) {
00302          if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
00303             colnames[namelen++] = '\\';
00304          }
00305          colnames[namelen++] = colname[i];
00306 
00307          if (namelen >= sizeof(colnames) - 2) {
00308             colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0';
00309             break;
00310          }
00311 
00312          if (colname[i] == '\0')
00313             break;
00314       }
00315 
00316       buflen = strlen(buf);
00317       res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
00318       if (indicator == SQL_NULL_DATA) {
00319          coldata[0] = '\0';
00320          res = SQL_SUCCESS;
00321       }
00322 
00323       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00324          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00325          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00326          ast_odbc_release_obj(obj);
00327          return -1;
00328       }
00329 
00330       /* Copy data, encoding '\' and ',' for the argument parser */
00331       for (i = 0; i < sizeof(coldata); i++) {
00332          if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
00333             buf[buflen++] = '\\';
00334          }
00335          buf[buflen++] = coldata[i];
00336 
00337          if (buflen >= len - 2)
00338             break;
00339 
00340          if (coldata[i] == '\0')
00341             break;
00342       }
00343 
00344       buf[buflen - 1] = ',';
00345       buf[buflen] = '\0';
00346    }
00347    /* Trim trailing comma */
00348    buf[buflen - 1] = '\0';
00349 
00350    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
00351 
00352    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00353    ast_odbc_release_obj(obj);
00354    return 0;
00355 }
00356 
00357 static int acf_escape(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
00358 {
00359    char *out = buf;
00360 
00361    for (; *data && out - buf < len; data++) {
00362       if (*data == '\'') {
00363          *out = '\'';
00364          out++;
00365       }
00366       *out++ = *data;
00367    }
00368    *out = '\0';
00369 
00370    return 0;
00371 }
00372 
00373 static struct ast_custom_function escape_function = {
00374    .name = "SQL_ESC",
00375    .synopsis = "Escapes single ticks for use in SQL statements",
00376    .syntax = "SQL_ESC(<string>)",
00377    .desc =
00378 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00379 "are otherwise used to delimit data.  For example:\n"
00380 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00381    .read = acf_escape,
00382    .write = NULL,
00383 };
00384 
00385 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00386 {
00387    const char *tmp;
00388    int i;
00389 
00390    if (!cfg || !catg) {
00391       return EINVAL;
00392    }
00393 
00394    *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00395    if (! (*query))
00396       return ENOMEM;
00397 
00398    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00399       char *tmp2 = ast_strdupa(tmp);
00400       AST_DECLARE_APP_ARGS(write,
00401          AST_APP_ARG(dsn)[5];
00402       );
00403       AST_NONSTANDARD_APP_ARGS(write, tmp2, ',');
00404       for (i = 0; i < 5; i++) {
00405          if (!ast_strlen_zero(write.dsn[i]))
00406             ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i]));
00407       }
00408    }
00409 
00410    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00411       char *tmp2 = ast_strdupa(tmp);
00412       AST_DECLARE_APP_ARGS(read,
00413          AST_APP_ARG(dsn)[5];
00414       );
00415       AST_NONSTANDARD_APP_ARGS(read, tmp2, ',');
00416       for (i = 0; i < 5; i++) {
00417          if (!ast_strlen_zero(read.dsn[i]))
00418             ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i]));
00419       }
00420    } else {
00421       /* If no separate readhandle, then use the writehandle for reading */
00422       for (i = 0; i < 5; i++) {
00423          if (!ast_strlen_zero((*query)->writehandle[i]))
00424             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00425       }
00426    }
00427 
00428    if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00429       ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00430       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00431    } else if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00432       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00433 
00434    if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00435       free(*query);
00436       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00437       return EINVAL;
00438    }
00439 
00440    if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00441       ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00442       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00443    } else if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00444       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00445 
00446    if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00447       free(*query);
00448       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00449       return EINVAL;
00450    }
00451 
00452    /* Allow escaping of embedded commas in fields to be turned off */
00453    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00454    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00455       if (ast_false(tmp))
00456          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00457    }
00458 
00459    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00460    if (! (*query)->acf) {
00461       free(*query);
00462       return ENOMEM;
00463    }
00464 
00465    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00466       asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
00467    } else {
00468       asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
00469    }
00470 
00471    if (!((*query)->acf->name)) {
00472       free((*query)->acf);
00473       free(*query);
00474       return ENOMEM;
00475    }
00476 
00477    asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00478 
00479    if (!((*query)->acf->syntax)) {
00480       free((char *)(*query)->acf->name);
00481       free((*query)->acf);
00482       free(*query);
00483       return ENOMEM;
00484    }
00485 
00486    (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00487    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00488       asprintf((char **)&((*query)->acf->desc),
00489                "Runs the following query, as defined in func_odbc.conf, performing\n"
00490                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00491                "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00492                "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00493                "\nRead:\n%s\n\nWrite:\n%s\n",
00494                (*query)->sql_read,
00495                (*query)->sql_write);
00496    } else if (!ast_strlen_zero((*query)->sql_read)) {
00497       asprintf((char **)&((*query)->acf->desc),
00498                "Runs the following query, as defined in func_odbc.conf, performing\n"
00499                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00500                "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00501                (*query)->sql_read);
00502    } else if (!ast_strlen_zero((*query)->sql_write)) {
00503       asprintf((char **)&((*query)->acf->desc),
00504                "Runs the following query, as defined in func_odbc.conf, performing\n"
00505                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00506                "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00507                "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00508                "This function may only be set.\nSQL:\n%s\n",
00509                (*query)->sql_write);
00510    } else {
00511       free((char *)(*query)->acf->syntax);
00512       free((char *)(*query)->acf->name);
00513       free((*query)->acf);
00514       free(*query);
00515       ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute.  Ignoring.\n", catg);
00516       return EINVAL;
00517    }
00518 
00519    if (! ((*query)->acf->desc)) {
00520       free((char *)(*query)->acf->syntax);
00521       free((char *)(*query)->acf->name);
00522       free((*query)->acf);
00523       free(*query);
00524       return ENOMEM;
00525    }
00526 
00527    if (ast_strlen_zero((*query)->sql_read)) {
00528       (*query)->acf->read = NULL;
00529    } else {
00530       (*query)->acf->read = acf_odbc_read;
00531    }
00532 
00533    if (ast_strlen_zero((*query)->sql_write)) {
00534       (*query)->acf->write = NULL;
00535    } else {
00536       (*query)->acf->write = acf_odbc_write;
00537    }
00538 
00539    return 0;
00540 }
00541 
00542 static int free_acf_query(struct acf_odbc_query *query)
00543 {
00544    if (query) {
00545       if (query->acf) {
00546          if (query->acf->name)
00547             free((char *)query->acf->name);
00548          if (query->acf->syntax)
00549             free((char *)query->acf->syntax);
00550          if (query->acf->desc)
00551             free((char *)query->acf->desc);
00552          free(query->acf);
00553       }
00554       free(query);
00555    }
00556    return 0;
00557 }
00558 
00559 static int load_module(void)
00560 {
00561    int res = 0;
00562    struct ast_config *cfg;
00563    char *catg;
00564 
00565    AST_LIST_LOCK(&queries);
00566 
00567    cfg = ast_config_load(config);
00568    if (!cfg) {
00569       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00570       AST_LIST_UNLOCK(&queries);
00571       return AST_MODULE_LOAD_DECLINE;
00572    }
00573 
00574    for (catg = ast_category_browse(cfg, NULL);
00575         catg;
00576         catg = ast_category_browse(cfg, catg)) {
00577       struct acf_odbc_query *query = NULL;
00578       int err;
00579 
00580       if ((err = init_acf_query(cfg, catg, &query))) {
00581          if (err == ENOMEM)
00582             ast_log(LOG_ERROR, "Out of memory\n");
00583          else if (err == EINVAL)
00584             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
00585          else
00586             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
00587       } else {
00588          AST_LIST_INSERT_HEAD(&queries, query, list);
00589          ast_custom_function_register(query->acf);
00590       }
00591    }
00592 
00593    ast_config_destroy(cfg);
00594    res |= ast_custom_function_register(&escape_function);
00595 
00596    AST_LIST_UNLOCK(&queries);
00597    return res;
00598 }
00599 
00600 static int unload_module(void)
00601 {
00602    struct acf_odbc_query *query;
00603    int res = 0;
00604 
00605    AST_LIST_LOCK(&queries);
00606    while (!AST_LIST_EMPTY(&queries)) {
00607       query = AST_LIST_REMOVE_HEAD(&queries, list);
00608       ast_custom_function_unregister(query->acf);
00609       free_acf_query(query);
00610    }
00611 
00612    res |= ast_custom_function_unregister(&escape_function);
00613 
00614    /* Allow any threads waiting for this lock to pass (avoids a race) */
00615    AST_LIST_UNLOCK(&queries);
00616    usleep(1);
00617    AST_LIST_LOCK(&queries);
00618 
00619    AST_LIST_UNLOCK(&queries);
00620    return 0;
00621 }
00622 
00623 static int reload(void)
00624 {
00625    int res = 0;
00626    struct ast_config *cfg;
00627    struct acf_odbc_query *oldquery;
00628    char *catg;
00629 
00630    AST_LIST_LOCK(&queries);
00631 
00632    while (!AST_LIST_EMPTY(&queries)) {
00633       oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
00634       ast_custom_function_unregister(oldquery->acf);
00635       free_acf_query(oldquery);
00636    }
00637 
00638    cfg = ast_config_load(config);
00639    if (!cfg) {
00640       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00641       goto reload_out;
00642    }
00643 
00644    for (catg = ast_category_browse(cfg, NULL);
00645         catg;
00646         catg = ast_category_browse(cfg, catg)) {
00647       struct acf_odbc_query *query = NULL;
00648 
00649       if (init_acf_query(cfg, catg, &query)) {
00650          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00651       } else {
00652          AST_LIST_INSERT_HEAD(&queries, query, list);
00653          ast_custom_function_register(query->acf);
00654       }
00655    }
00656 
00657    ast_config_destroy(cfg);
00658 reload_out:
00659    AST_LIST_UNLOCK(&queries);
00660    return res;
00661 }
00662 
00663 /* XXX need to revise usecount - set if query_lock is set */
00664 
00665 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00666       .load = load_module,
00667       .unload = unload_module,
00668       .reload = reload,
00669           );
00670 

Generated on Mon May 14 04:42:58 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1