Fri Sep 29 11:12:30 2006

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 Tilghman Lesher
00005  *
00006  * Tilghman Lesher <func_odbc__200604@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__200604@the-tilghman.com>
00025  */
00026 
00027 #include <sys/types.h>
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <unistd.h>
00031 #include <string.h>
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7 $")
00036 
00037 #include <asterisk/module.h>
00038 #include <asterisk/file.h>
00039 #include <asterisk/logger.h>
00040 #include <asterisk/options.h>
00041 #include <asterisk/channel.h>
00042 #include <asterisk/pbx.h>
00043 #include <asterisk/module.h>
00044 #include <asterisk/config.h>
00045 #include <asterisk/res_odbc.h>
00046 #include <asterisk/app.h>
00047 
00048 static char *tdesc = "ODBC lookups";
00049 
00050 static char *config = "func_odbc.conf";
00051 
00052 struct acf_odbc_query {
00053    AST_LIST_ENTRY(acf_odbc_query) list;
00054    char dsn[30];
00055    char sql_read[2048];
00056    char sql_write[2048];
00057    struct ast_custom_function *acf;
00058 };
00059 
00060 AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
00061 
00062 #ifdef NEEDTRACE
00063 static void acf_odbc_error(SQLHSTMT stmt, int res)
00064 {
00065    char state[10] = "", diagnostic[256] = "";
00066    SQLINTEGER nativeerror = 0;
00067    SQLSMALLINT diagbytes = 0;
00068    SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00069    ast_log(LOG_WARNING, "SQL return value %d: error %s: %s (len %d)\n", res, state, diagnostic, diagbytes);
00070 }
00071 #endif
00072 
00073 /*
00074  * Master control routine
00075  */
00076 static void acf_odbc_write(struct ast_channel *chan, char *cmd, char *data, const char *value)
00077 {
00078    odbc_obj *obj;
00079    struct acf_odbc_query *query;
00080    char *s, *t, *arg, buf[2048]="", varname[15];
00081    int res, argcount=0, valcount=0, i, retry=0;
00082    struct ast_channel *ast;
00083    SQLHSTMT stmt;
00084    SQLINTEGER nativeerror=0, numfields=0, rows=0;
00085    SQLSMALLINT diagbytes=0;
00086    unsigned char state[10], diagnostic[256];
00087 #ifdef NEEDTRACE
00088    SQLINTEGER enable = 1;
00089    char *tracefile = "/tmp/odbc.trace";
00090 #endif
00091 
00092    AST_LIST_LOCK(&queries);
00093    AST_LIST_TRAVERSE(&queries, query, list) {
00094       if (!strcmp(query->acf->name, cmd)) {
00095          break;
00096       }
00097    }
00098 
00099    if (!query) {
00100       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00101       AST_LIST_UNLOCK(&queries);
00102       return;
00103    }
00104 
00105    obj = fetch_odbc_obj(query->dsn, 0);
00106 
00107    if (!obj) {
00108       ast_log(LOG_ERROR, "No such DSN registered: %s (check res_odbc.conf)\n", query->dsn);
00109       AST_LIST_UNLOCK(&queries);
00110       return;
00111    }
00112 
00113    /* Parse our arguments */
00114    s = ast_strdupa(data);
00115    if (value) {
00116       t = ast_strdupa(value);
00117    } else {
00118       t = "";
00119    }
00120 
00121    if (!s || !t) {
00122       ast_log(LOG_ERROR, "Out of memory\n");
00123       AST_LIST_UNLOCK(&queries);
00124       return;
00125    }
00126 
00127    /* XXX You might be tempted to change this section into using
00128     * pbx_builtin_pushvar_helper().  However, note that if you try
00129     * to set a NULL (like for VALUE), then nothing gets set, and the
00130     * value doesn't get masked out.  Even worse, when you subsequently
00131     * try to remove the value you just set, you'll wind up unsetting
00132     * the previous value (which is wholly undesireable).  Hence, this
00133     * has to remain the way it is done here. XXX
00134     */
00135 
00136    /* Save old arguments as variables in a fake channel */
00137    ast = ast_channel_alloc(0);
00138    while ((arg = strsep(&s, "|"))) {
00139       argcount++;
00140       snprintf(varname, sizeof(varname), "ARG%d", argcount);
00141       pbx_builtin_setvar_helper(ast, varname, pbx_builtin_getvar_helper(chan, varname));
00142       pbx_builtin_setvar_helper(chan, varname, arg);
00143    }
00144 
00145    /* Parse values, just like arguments */
00146    while ((arg = strsep(&t, "|"))) {
00147       valcount++;
00148       snprintf(varname, sizeof(varname), "VAL%d", valcount);
00149       pbx_builtin_setvar_helper(ast, varname, pbx_builtin_getvar_helper(chan, varname));
00150       pbx_builtin_setvar_helper(chan, varname, arg);
00151    }
00152 
00153    /* Additionally set the value as a whole */
00154    /* Note that pbx_builtin_setvar_helper will quite happily take a NULL for the 3rd argument */
00155    pbx_builtin_setvar_helper(ast, "VALUE", pbx_builtin_getvar_helper(chan, "VALUE"));
00156    pbx_builtin_setvar_helper(chan, "VALUE", value);
00157 
00158    pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
00159 
00160    /* Restore prior values */
00161    for (i=1; i<=argcount; i++) {
00162       snprintf(varname, sizeof(varname), "ARG%d", argcount);
00163       pbx_builtin_setvar_helper(chan, varname, pbx_builtin_getvar_helper(ast, varname));
00164    }
00165 
00166    for (i=1; i<=valcount; i++) {
00167       snprintf(varname, sizeof(varname), "VAL%d", argcount);
00168       pbx_builtin_setvar_helper(chan, varname, pbx_builtin_getvar_helper(ast, varname));
00169    }
00170    pbx_builtin_setvar_helper(chan, "VALUE", pbx_builtin_getvar_helper(ast, "VALUE"));
00171 
00172    ast_channel_free(ast);
00173    AST_LIST_UNLOCK(&queries);
00174 
00175 retry_write:
00176 #ifdef NEEDTRACE
00177    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00178    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00179 #endif
00180 
00181    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00182    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00183       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00184       pbx_builtin_setvar_helper(chan, "ODBCROWS", "-1");
00185       return;
00186    }
00187 
00188    res = SQLPrepare(stmt, (unsigned char *)buf, SQL_NTS);
00189    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00190       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", buf);
00191       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00192       pbx_builtin_setvar_helper(chan, "ODBCROWS", "-1");
00193       return;
00194    }
00195 
00196    res = SQLExecute(stmt);
00197    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00198       if (res == SQL_ERROR) {
00199          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00200          for (i = 0; i <= numfields; i++) {
00201             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00202             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00203             if (i > 10) {
00204                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00205                break;
00206             }
00207          }
00208       }
00209       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00210       odbc_obj_disconnect(obj);
00211       /* All handles are now invalid (after a disconnect), so we gotta redo all handles */
00212       odbc_obj_connect(obj);
00213       if (!retry) {
00214          retry = 1;
00215          goto retry_write;
00216       }
00217       rows = -1;
00218    } else {
00219       /* Rows affected */
00220       SQLRowCount(stmt, &rows);
00221    }
00222 
00223    /* Output the affected rows, for all cases.  In the event of failure, we
00224     * flag this as -1 rows.  Note that this is different from 0 affected rows
00225     * which would be the case if we succeeded in our query, but the values did
00226     * not change. */
00227    snprintf(varname, sizeof(varname), "%d", (int)rows);
00228    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00229 
00230    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00231       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", buf);
00232    }
00233 
00234    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00235 }
00236 
00237 static char *acf_odbc_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
00238 {
00239    odbc_obj *obj;
00240    struct acf_odbc_query *query;
00241    char *s, *arg, sql[2048] = "", varname[15];
00242    int count=0, res, x, buflen = 0;
00243    SQLHSTMT stmt;
00244    SQLSMALLINT colcount=0;
00245    SQLINTEGER indicator;
00246 #ifdef NEEDTRACE
00247    SQLINTEGER enable = 1;
00248    char *tracefile = "/tmp/odbc.trace";
00249 #endif
00250 
00251    AST_LIST_LOCK(&queries);
00252    AST_LIST_TRAVERSE(&queries, query, list) {
00253       if (!strcmp(query->acf->name, cmd)) {
00254          break;
00255       }
00256    }
00257 
00258    if (!query) {
00259       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00260       AST_LIST_UNLOCK(&queries);
00261       return "";
00262    }
00263 
00264    obj = fetch_odbc_obj(query->dsn, 0);
00265 
00266    if (!obj) {
00267       ast_log(LOG_ERROR, "No such DSN registered: %s (check res_odbc.conf)\n", query->dsn);
00268       AST_LIST_UNLOCK(&queries);
00269       return "";
00270    }
00271 
00272 #ifdef NEEDTRACE
00273    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
00274    SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
00275 #endif
00276 
00277    /* Parse our arguments */
00278    if (!(s = ast_strdupa(data))) {
00279       AST_LIST_UNLOCK(&queries);
00280       return "";
00281    }
00282 
00283    while ((arg = strsep(&s, "|"))) {
00284       count++;
00285       snprintf(varname, sizeof(varname), "ARG%d", count);
00286       /* arg is by definition non-NULL, so this works, here */
00287       pbx_builtin_pushvar_helper(chan, varname, arg);
00288    }
00289 
00290    pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
00291 
00292    /* Restore prior values */
00293    for (x = 1; x <= count; x++) {
00294       snprintf(varname, sizeof(varname), "ARG%d", x);
00295       pbx_builtin_setvar_helper(chan, varname, NULL);
00296    }
00297 
00298    AST_LIST_UNLOCK(&queries);
00299 
00300    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00301    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00302       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00303       return "";
00304    }
00305 
00306    res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
00307    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00308       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00309       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00310       return "";
00311    }
00312 
00313    res = odbc_smart_execute(obj, stmt);
00314    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00315       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
00316       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00317       return "";
00318    }
00319 
00320    res = SQLNumResultCols(stmt, &colcount);
00321    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00322       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00323       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00324       return "";
00325    }
00326 
00327    memset(buf, 0, len);
00328 
00329    res = SQLFetch(stmt);
00330    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00331       if (res == SQL_NO_DATA) {
00332          if (option_verbose > 3) {
00333             ast_verbose(VERBOSE_PREFIX_4 "Found no rows [%s]\n", sql);
00334          }
00335       } else if (option_verbose > 3) {
00336          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
00337       }
00338       goto acf_out;
00339    }
00340 
00341    for (x = 0; x < colcount; x++) {
00342       int i;
00343       char coldata[256];
00344 
00345       buflen = strlen(buf);
00346       res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
00347       if (indicator == SQL_NULL_DATA) {
00348          coldata[0] = '\0';
00349          res = SQL_SUCCESS;
00350       }
00351 
00352       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00353          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00354          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00355          return "";
00356       }
00357 
00358       /* Copy data, encoding '\' and ',' for the argument parser */
00359       for (i = 0; i < sizeof(coldata); i++) {
00360          if (coldata[i] == '\\' || coldata[i] == ',') {
00361             buf[buflen++] = '\\';
00362          }
00363          buf[buflen++] = coldata[i];
00364 
00365          if (buflen >= len - 2) {
00366             buf[buflen >= len ? len - 1 : buflen] = '\0';
00367             break;
00368          }
00369 
00370          if (coldata[i] == '\0')
00371             break;
00372       }
00373 
00374       buf[buflen - 1] = ',';
00375    }
00376    /* Trim trailing comma */
00377    buf[buflen - 1] = '\0';
00378 
00379 acf_out:
00380    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00381    return buf;
00382 }
00383 
00384 static char *acf_escape(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
00385 {
00386    char *in, *out = buf;
00387    for (in = data; *in && out - buf < len; in++) {
00388       if (*in == '\'') {
00389          *out = '\'';
00390          out++;
00391       }
00392       *out = *in;
00393       out++;
00394    }
00395    *out = '\0';
00396    return buf;
00397 }
00398 
00399 static struct ast_custom_function escape_function = {
00400    .name = "SQL_ESC",
00401    .synopsis = "Escapes single ticks for use in SQL statements",
00402    .syntax = "SQL_ESC(<string>)",
00403    .desc =
00404 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00405 "are otherwise used to delimit data.  For example:\n"
00406 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00407    .read = acf_escape,
00408    .write = NULL,
00409 };
00410 
00411 
00412 
00413 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00414 {
00415    char *tmp;
00416 
00417    if (!cfg || !catg) {
00418       return -1;
00419    }
00420 
00421    *query = calloc(1, sizeof(struct acf_odbc_query));
00422    if (! (*query))
00423       return -1;
00424 
00425    if ((tmp = ast_variable_retrieve(cfg, catg, "dsn"))) {
00426       ast_copy_string((*query)->dsn, tmp, sizeof((*query)->dsn));
00427    } else {
00428       return -1;
00429    }
00430 
00431    if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00432       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00433    }
00434 
00435    if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00436       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00437    }
00438 
00439    (*query)->acf = calloc(1, sizeof(struct ast_custom_function));
00440    if (! (*query)->acf) {
00441       free(*query);
00442       return -1;
00443    }
00444 
00445    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00446       asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
00447    } else {
00448       asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
00449    }
00450 
00451    if (!((*query)->acf->name)) {
00452       free((*query)->acf);
00453       free(*query);
00454       return -1;
00455    }
00456 
00457    asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00458 
00459    if (!((*query)->acf->syntax)) {
00460       free((char *)(*query)->acf->name);
00461       free((*query)->acf);
00462       free(*query);
00463       return -1;
00464    }
00465 
00466    (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00467    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00468       asprintf((char **)&((*query)->acf->desc),
00469                "Runs the following query, as defined in func_odbc.conf, performing\n"
00470                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00471                "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00472                "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00473                "\nRead:\n%s\n\nWrite:\n%s\n",
00474                (*query)->sql_read,
00475                (*query)->sql_write);
00476    } else if (!ast_strlen_zero((*query)->sql_read)) {
00477       asprintf((char **)&((*query)->acf->desc),
00478                "Runs the following query, as defined in func_odbc.conf, performing\n"
00479                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00480                "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00481                (*query)->sql_read);
00482    } else if (!ast_strlen_zero((*query)->sql_write)) {
00483       asprintf((char **)&((*query)->acf->desc),
00484                "Runs the following query, as defined in func_odbc.conf, performing\n"
00485                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00486                "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00487                "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00488                "This function may only be set.\nSQL:\n%s\n",
00489                (*query)->sql_write);
00490    }
00491 
00492    /* Could be out of memory, or could be we have neither sql_read nor sql_write */
00493    if (! ((*query)->acf->desc)) {
00494       free((char *)(*query)->acf->syntax);
00495       free((char *)(*query)->acf->name);
00496       free((*query)->acf);
00497       free(*query);
00498       return -1;
00499    }
00500 
00501    if (ast_strlen_zero((*query)->sql_read)) {
00502       (*query)->acf->read = NULL;
00503    } else {
00504       (*query)->acf->read = acf_odbc_read;
00505    }
00506 
00507    if (ast_strlen_zero((*query)->sql_write)) {
00508       (*query)->acf->write = NULL;
00509    } else {
00510       (*query)->acf->write = acf_odbc_write;
00511    }
00512 
00513    return 0;
00514 }
00515 
00516 static int free_acf_query(struct acf_odbc_query *query)
00517 {
00518    if (query) {
00519       if (query->acf) {
00520          if (query->acf->name)
00521             free(query->acf->name);
00522          if (query->acf->syntax)
00523             free(query->acf->syntax);
00524          if (query->acf->desc)
00525             free(query->acf->desc);
00526          free(query->acf);
00527       }
00528       free(query);
00529    }
00530    return 0;
00531 }
00532 
00533 static int odbc_load_module(void)
00534 {
00535    int res = 0;
00536    struct ast_config *cfg;
00537    char *catg;
00538 
00539    AST_LIST_LOCK(&queries);
00540 
00541    cfg = ast_config_load(config);
00542    if (!cfg) {
00543       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00544       AST_LIST_UNLOCK(&queries);
00545       return -1;
00546    }
00547 
00548    for (catg = ast_category_browse(cfg, NULL);
00549         catg;
00550         catg = ast_category_browse(cfg, catg)) {
00551       struct acf_odbc_query *query = NULL;
00552 
00553       if (init_acf_query(cfg, catg, &query)) {
00554          ast_log(LOG_ERROR, "Out of memory\n");
00555          free_acf_query(query);
00556       } else {
00557          AST_LIST_INSERT_HEAD(&queries, query, list);
00558          ast_custom_function_register(query->acf);
00559       }
00560    }
00561 
00562    ast_config_destroy(cfg);
00563    ast_custom_function_register(&escape_function);
00564 
00565    AST_LIST_UNLOCK(&queries);
00566    return res;
00567 }
00568 
00569 static int odbc_unload_module(void)
00570 {
00571    struct acf_odbc_query *query;
00572 
00573    AST_LIST_LOCK(&queries);
00574    while (!AST_LIST_EMPTY(&queries)) {
00575       query = AST_LIST_REMOVE_HEAD(&queries, list);
00576       ast_custom_function_unregister(query->acf);
00577       free_acf_query(query);
00578    }
00579 
00580    ast_custom_function_unregister(&escape_function);
00581 
00582    /* Allow any threads waiting for this lock to pass (avoids a race) */
00583    AST_LIST_UNLOCK(&queries);
00584    AST_LIST_LOCK(&queries);
00585 
00586    AST_LIST_UNLOCK(&queries);
00587    return 0;
00588 }
00589 
00590 int reload(void)
00591 {
00592    int res = 0;
00593    struct ast_config *cfg;
00594    struct acf_odbc_query *oldquery;
00595    char *catg;
00596 
00597    AST_LIST_LOCK(&queries);
00598 
00599    while (!AST_LIST_EMPTY(&queries)) {
00600       oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
00601       ast_custom_function_unregister(oldquery->acf);
00602       free_acf_query(oldquery);
00603    }
00604 
00605    cfg = ast_config_load(config);
00606    if (!cfg) {
00607       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00608       goto reload_out;
00609    }
00610 
00611    for (catg = ast_category_browse(cfg, NULL);
00612         catg;
00613         catg = ast_category_browse(cfg, catg)) {
00614       struct acf_odbc_query *query = NULL;
00615 
00616       if (init_acf_query(cfg, catg, &query)) {
00617          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00618       } else {
00619          AST_LIST_INSERT_HEAD(&queries, query, list);
00620          ast_custom_function_register(query->acf);
00621       }
00622    }
00623 
00624    ast_config_destroy(cfg);
00625 reload_out:
00626    AST_LIST_UNLOCK(&queries);
00627    return res;
00628 }
00629 
00630 int unload_module(void)
00631 {
00632    return odbc_unload_module();
00633 }
00634 
00635 int load_module(void)
00636 {
00637    return odbc_load_module();
00638 }
00639 
00640 char *description(void)
00641 {
00642    return tdesc;
00643 }
00644 
00645 int usecount(void)
00646 {
00647    if (! ast_mutex_trylock(&(&queries)->lock)) {
00648       ast_mutex_unlock(&(&queries)->lock);
00649       return 0;
00650    } else {
00651       return 1;
00652    }
00653 }
00654 
00655 char *key()
00656 {
00657    return ASTERISK_GPL_KEY;
00658 }

Generated on Fri Sep 29 11:12:30 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7