2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (c) 2005, 2006 Tilghman Lesher
6 * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
24 * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
30 <depend>unixodbc</depend>
32 <depend>res_odbc</depend>
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include "asterisk/module.h"
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/config.h"
44 #include "asterisk/res_odbc.h"
45 #include "asterisk/app.h"
47 static char *config = "func_odbc.conf";
50 OPT_ESCAPECOMMAS = (1 << 0),
51 OPT_MULTIROW = (1 << 1),
54 struct acf_odbc_query {
55 AST_LIST_ENTRY(acf_odbc_query) list;
56 char readhandle[5][30];
57 char writehandle[5][30];
62 struct ast_custom_function *acf;
65 static void odbc_datastore_free(void *data);
67 struct ast_datastore_info odbc_info = {
69 .destroy = odbc_datastore_free,
72 /* For storing each result row */
73 struct odbc_datastore_row {
74 AST_LIST_ENTRY(odbc_datastore_row) list;
78 /* For storing each result set */
79 struct odbc_datastore {
80 AST_LIST_HEAD(, odbc_datastore_row);
84 AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
86 static int resultcount = 0;
88 static void odbc_datastore_free(void *data)
90 struct odbc_datastore *result = data;
91 struct odbc_datastore_row *row;
92 AST_LIST_LOCK(result);
93 while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
96 AST_LIST_UNLOCK(result);
97 AST_LIST_HEAD_DESTROY(result);
101 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
107 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
108 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
109 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
113 res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
114 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
115 ast_log(LOG_WARNING, "SQL Exec Direct failed![%s]\n", sql);
116 SQLCloseCursor(stmt);
117 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
125 * Master control routine
127 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
129 struct odbc_obj *obj = NULL;
130 struct acf_odbc_query *query;
131 char *t, buf[2048], varname[15];
132 int i, dsn, bogus_chan = 0;
133 AST_DECLARE_APP_ARGS(values,
134 AST_APP_ARG(field)[100];
136 AST_DECLARE_APP_ARGS(args,
137 AST_APP_ARG(field)[100];
139 SQLHSTMT stmt = NULL;
142 AST_LIST_LOCK(&queries);
143 AST_LIST_TRAVERSE(&queries, query, list) {
144 if (!strcmp(query->acf->name, cmd)) {
150 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
151 AST_LIST_UNLOCK(&queries);
156 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
161 ast_autoservice_start(chan);
163 /* Parse our arguments */
164 t = value ? ast_strdupa(value) : "";
167 ast_log(LOG_ERROR, "Out of memory\n");
168 AST_LIST_UNLOCK(&queries);
170 ast_autoservice_stop(chan);
172 ast_channel_free(chan);
176 AST_STANDARD_APP_ARGS(args, s);
177 for (i = 0; i < args.argc; i++) {
178 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
179 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
182 /* Parse values, just like arguments */
183 AST_STANDARD_APP_ARGS(values, t);
184 for (i = 0; i < values.argc; i++) {
185 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
186 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
189 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
190 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
192 pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
194 /* Restore prior values */
195 for (i = 0; i < args.argc; i++) {
196 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
197 pbx_builtin_setvar_helper(chan, varname, NULL);
200 for (i = 0; i < values.argc; i++) {
201 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
202 pbx_builtin_setvar_helper(chan, varname, NULL);
204 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
206 AST_LIST_UNLOCK(&queries);
208 for (dsn = 0; dsn < 5; dsn++) {
209 if (!ast_strlen_zero(query->writehandle[dsn])) {
210 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
212 stmt = ast_odbc_direct_execute(obj, generic_execute, buf);
220 SQLRowCount(stmt, &rows);
223 /* Output the affected rows, for all cases. In the event of failure, we
224 * flag this as -1 rows. Note that this is different from 0 affected rows
225 * which would be the case if we succeeded in our query, but the values did
227 snprintf(varname, sizeof(varname), "%d", (int)rows);
228 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
231 SQLCloseCursor(stmt);
232 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
235 ast_odbc_release_obj(obj);
238 ast_autoservice_stop(chan);
240 ast_channel_free(chan);
245 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
247 struct odbc_obj *obj = NULL;
248 struct acf_odbc_query *query;
249 char sql[2048], varname[15], colnames[2048] = "", rowcount[12] = "-1";
250 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
251 AST_DECLARE_APP_ARGS(args,
252 AST_APP_ARG(field)[100];
254 SQLHSTMT stmt = NULL;
255 SQLSMALLINT colcount=0;
257 SQLSMALLINT collength;
258 struct odbc_datastore *resultset = NULL;
259 struct odbc_datastore_row *row = NULL;
261 AST_LIST_LOCK(&queries);
262 AST_LIST_TRAVERSE(&queries, query, list) {
263 if (!strcmp(query->acf->name, cmd)) {
269 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
270 AST_LIST_UNLOCK(&queries);
271 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
276 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
281 ast_autoservice_start(chan);
283 AST_STANDARD_APP_ARGS(args, s);
284 for (x = 0; x < args.argc; x++) {
285 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
286 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
289 pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
291 /* Restore prior values */
292 for (x = 0; x < args.argc; x++) {
293 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
294 pbx_builtin_setvar_helper(chan, varname, NULL);
297 /* Save these flags, so we can release the lock */
298 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
299 if (ast_test_flag(query, OPT_MULTIROW)) {
300 resultset = ast_calloc(1, sizeof(*resultset));
301 AST_LIST_HEAD_INIT(resultset);
303 rowlimit = query->rowlimit;
307 AST_LIST_UNLOCK(&queries);
309 for (dsn = 0; dsn < 5; dsn++) {
310 if (!ast_strlen_zero(query->readhandle[dsn])) {
311 obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
313 stmt = ast_odbc_direct_execute(obj, generic_execute, sql);
320 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql);
322 ast_odbc_release_obj(obj);
323 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
325 ast_autoservice_stop(chan);
327 ast_channel_free(chan);
331 res = SQLNumResultCols(stmt, &colcount);
332 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
333 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
334 SQLCloseCursor(stmt);
335 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
336 ast_odbc_release_obj(obj);
337 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
339 ast_autoservice_stop(chan);
341 ast_channel_free(chan);
345 res = SQLFetch(stmt);
346 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
348 if (res == SQL_NO_DATA) {
349 ast_verb(4, "Found no rows [%s]\n", sql);
351 ast_copy_string(rowcount, "0", sizeof(rowcount));
353 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
355 SQLCloseCursor(stmt);
356 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
357 ast_odbc_release_obj(obj);
358 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
360 ast_autoservice_stop(chan);
362 ast_channel_free(chan);
366 for (y = 0; y < rowlimit; y++) {
368 for (x = 0; x < colcount; x++) {
376 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
377 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
378 snprintf(colname, sizeof(colname), "field%d", x);
381 if (!ast_strlen_zero(colnames))
382 strncat(colnames, ",", sizeof(colnames) - strlen(colnames) - 1);
383 namelen = strlen(colnames);
385 /* Copy data, encoding '\' and ',' for the argument parser */
386 for (i = 0; i < sizeof(colname); i++) {
387 if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
388 colnames[namelen++] = '\\';
390 colnames[namelen++] = colname[i];
392 if (namelen >= sizeof(colnames) - 2) {
393 colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0';
397 if (colname[i] == '\0')
402 void *tmp = ast_realloc(resultset, sizeof(*resultset) + strlen(colnames) + 1);
404 ast_log(LOG_ERROR, "No space for a new resultset?\n");
406 SQLCloseCursor(stmt);
407 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
408 ast_odbc_release_obj(obj);
409 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
411 ast_autoservice_stop(chan);
413 ast_channel_free(chan);
417 strcpy((char *)resultset + sizeof(*resultset), colnames);
421 buflen = strlen(buf);
422 res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
423 if (indicator == SQL_NULL_DATA) {
428 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
429 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
434 /* Copy data, encoding '\' and ',' for the argument parser */
435 for (i = 0; i < sizeof(coldata); i++) {
436 if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
437 buf[buflen++] = '\\';
439 buf[buflen++] = coldata[i];
441 if (buflen >= len - 2)
444 if (coldata[i] == '\0')
448 buf[buflen - 1] = ',';
451 /* Trim trailing comma */
452 buf[buflen - 1] = '\0';
455 row = ast_calloc(1, sizeof(*row) + buflen);
457 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
460 strcpy((char *)row + sizeof(*row), buf);
461 AST_LIST_INSERT_TAIL(resultset, row, list);
464 res = SQLFetch(stmt);
465 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
466 if (res != SQL_NO_DATA)
467 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
475 snprintf(rowcount, sizeof(rowcount), "%d", y);
476 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
477 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
480 struct ast_datastore *odbc_store;
481 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
482 snprintf(buf, len, "%d", uid);
483 odbc_store = ast_datastore_alloc(&odbc_info, buf);
485 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
486 odbc_datastore_free(resultset);
487 SQLCloseCursor(stmt);
488 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
489 ast_odbc_release_obj(obj);
491 ast_autoservice_stop(chan);
493 ast_channel_free(chan);
496 odbc_store->data = resultset;
497 ast_channel_datastore_add(chan, odbc_store);
499 SQLCloseCursor(stmt);
500 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
501 ast_odbc_release_obj(obj);
503 ast_autoservice_stop(chan);
505 ast_channel_free(chan);
509 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
513 for (; *data && out - buf < len; data++) {
525 static struct ast_custom_function escape_function = {
527 .synopsis = "Escapes single ticks for use in SQL statements",
528 .syntax = "SQL_ESC(<string>)",
530 "Used in SQL templates to escape data which may contain single ticks (') which\n"
531 "are otherwise used to delimit data. For example:\n"
532 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
537 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
539 struct ast_datastore *store;
540 struct odbc_datastore *resultset;
541 struct odbc_datastore_row *row;
542 store = ast_channel_datastore_find(chan, &odbc_info, data);
546 resultset = store->data;
547 AST_LIST_LOCK(resultset);
548 row = AST_LIST_REMOVE_HEAD(resultset, list);
549 AST_LIST_UNLOCK(resultset);
551 /* Cleanup datastore */
552 ast_channel_datastore_remove(chan, store);
553 ast_datastore_free(store);
556 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
557 ast_copy_string(buf, row->data, len);
562 static struct ast_custom_function fetch_function = {
563 .name = "ODBC_FETCH",
564 .synopsis = "Fetch a row from a multirow query",
565 .syntax = "ODBC_FETCH(<result-id>)",
567 "For queries which are marked as mode=multirow, the original query returns a\n"
568 "result-id from which results may be fetched. This function implements the\n"
569 "actual fetch of the results.\n",
574 static char *app_odbcfinish = "ODBCFinish";
575 static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
576 static char *desc_odbcfinish =
577 "ODBCFinish(<result-id>)\n"
578 " Clears any remaining rows of the specified resultset\n";
581 static int exec_odbcfinish(struct ast_channel *chan, void *data)
583 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
584 if (!store) /* Already freed; no big deal. */
586 ast_channel_datastore_remove(chan, store);
587 ast_datastore_free(store);
591 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
600 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
604 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
605 char *tmp2 = ast_strdupa(tmp);
606 AST_DECLARE_APP_ARGS(write,
609 AST_STANDARD_APP_ARGS(write, tmp2);
610 for (i = 0; i < 5; i++) {
611 if (!ast_strlen_zero(write.dsn[i]))
612 ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i]));
616 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
617 char *tmp2 = ast_strdupa(tmp);
618 AST_DECLARE_APP_ARGS(read,
621 AST_STANDARD_APP_ARGS(read, tmp2);
622 for (i = 0; i < 5; i++) {
623 if (!ast_strlen_zero(read.dsn[i]))
624 ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i]));
627 /* If no separate readhandle, then use the writehandle for reading */
628 for (i = 0; i < 5; i++) {
629 if (!ast_strlen_zero((*query)->writehandle[i]))
630 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
634 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
635 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
636 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
637 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
638 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
641 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
644 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
648 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
649 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
650 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
651 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
652 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
655 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
658 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
662 /* Allow escaping of embedded commas in fields to be turned off */
663 ast_set_flag((*query), OPT_ESCAPECOMMAS);
664 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
666 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
669 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
670 if (strcasecmp(tmp, "multirow") == 0)
671 ast_set_flag((*query), OPT_MULTIROW);
672 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
673 sscanf(tmp, "%d", &((*query)->rowlimit));
676 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
677 if (! (*query)->acf) {
683 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
684 asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
686 asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
689 if (!((*query)->acf->name)) {
690 ast_free((*query)->acf);
696 asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
698 if (!((*query)->acf->syntax)) {
699 ast_free((char *)(*query)->acf->name);
700 ast_free((*query)->acf);
706 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
707 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
708 asprintf((char **)&((*query)->acf->desc),
709 "Runs the following query, as defined in func_odbc.conf, performing\n"
710 "substitution of the arguments into the query as specified by ${ARG1},\n"
711 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
712 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
713 "\nRead:\n%s\n\nWrite:\n%s\n",
715 (*query)->sql_write);
716 } else if (!ast_strlen_zero((*query)->sql_read)) {
717 asprintf((char **)&((*query)->acf->desc),
718 "Runs the following query, as defined in func_odbc.conf, performing\n"
719 "substitution of the arguments into the query as specified by ${ARG1},\n"
720 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
722 } else if (!ast_strlen_zero((*query)->sql_write)) {
723 asprintf((char **)&((*query)->acf->desc),
724 "Runs the following query, as defined in func_odbc.conf, performing\n"
725 "substitution of the arguments into the query as specified by ${ARG1},\n"
726 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
727 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
728 "This function may only be set.\nSQL:\n%s\n",
729 (*query)->sql_write);
731 ast_free((char *)(*query)->acf->syntax);
732 ast_free((char *)(*query)->acf->name);
733 ast_free((*query)->acf);
735 ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute. Ignoring.\n", catg);
739 if (! ((*query)->acf->desc)) {
740 ast_free((char *)(*query)->acf->syntax);
741 ast_free((char *)(*query)->acf->name);
742 ast_free((*query)->acf);
748 if (ast_strlen_zero((*query)->sql_read)) {
749 (*query)->acf->read = NULL;
751 (*query)->acf->read = acf_odbc_read;
754 if (ast_strlen_zero((*query)->sql_write)) {
755 (*query)->acf->write = NULL;
757 (*query)->acf->write = acf_odbc_write;
763 static int free_acf_query(struct acf_odbc_query *query)
767 if (query->acf->name)
768 ast_free((char *)query->acf->name);
769 if (query->acf->syntax)
770 ast_free((char *)query->acf->syntax);
771 if (query->acf->desc)
772 ast_free((char *)query->acf->desc);
773 ast_free(query->acf);
780 static int load_module(void)
783 struct ast_config *cfg;
785 struct ast_flags config_flags = { 0 };
787 res |= ast_custom_function_register(&fetch_function);
788 res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
789 AST_LIST_LOCK(&queries);
791 cfg = ast_config_load(config, config_flags);
793 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
794 AST_LIST_UNLOCK(&queries);
795 return AST_MODULE_LOAD_DECLINE;
798 for (catg = ast_category_browse(cfg, NULL);
800 catg = ast_category_browse(cfg, catg)) {
801 struct acf_odbc_query *query = NULL;
804 if ((err = init_acf_query(cfg, catg, &query))) {
806 ast_log(LOG_ERROR, "Out of memory\n");
807 else if (err == EINVAL)
808 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
810 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
812 AST_LIST_INSERT_HEAD(&queries, query, list);
813 ast_custom_function_register(query->acf);
817 ast_config_destroy(cfg);
818 res |= ast_custom_function_register(&escape_function);
820 AST_LIST_UNLOCK(&queries);
824 static int unload_module(void)
826 struct acf_odbc_query *query;
829 AST_LIST_LOCK(&queries);
830 while (!AST_LIST_EMPTY(&queries)) {
831 query = AST_LIST_REMOVE_HEAD(&queries, list);
832 ast_custom_function_unregister(query->acf);
833 free_acf_query(query);
836 res |= ast_custom_function_unregister(&escape_function);
837 res |= ast_custom_function_unregister(&fetch_function);
838 res |= ast_unregister_application(app_odbcfinish);
840 /* Allow any threads waiting for this lock to pass (avoids a race) */
841 AST_LIST_UNLOCK(&queries);
843 AST_LIST_LOCK(&queries);
845 AST_LIST_UNLOCK(&queries);
849 static int reload(void)
852 struct ast_config *cfg;
853 struct acf_odbc_query *oldquery;
855 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
857 cfg = ast_config_load(config, config_flags);
858 if (cfg == CONFIG_STATUS_FILEUNCHANGED)
861 AST_LIST_LOCK(&queries);
863 while (!AST_LIST_EMPTY(&queries)) {
864 oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
865 ast_custom_function_unregister(oldquery->acf);
866 free_acf_query(oldquery);
870 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
874 for (catg = ast_category_browse(cfg, NULL);
876 catg = ast_category_browse(cfg, catg)) {
877 struct acf_odbc_query *query = NULL;
879 if (init_acf_query(cfg, catg, &query)) {
880 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
882 AST_LIST_INSERT_HEAD(&queries, query, list);
883 ast_custom_function_register(query->acf);
887 ast_config_destroy(cfg);
889 AST_LIST_UNLOCK(&queries);
893 /* XXX need to revise usecount - set if query_lock is set */
895 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
897 .unload = unload_module,