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_RWLIST_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_RWLIST_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, 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;
141 struct ast_str *buf = ast_str_create(16);
147 AST_RWLIST_RDLOCK(&queries);
148 AST_RWLIST_TRAVERSE(&queries, query, list) {
149 if (!strcmp(query->acf->name, cmd)) {
155 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
156 AST_RWLIST_UNLOCK(&queries);
162 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
167 ast_autoservice_start(chan);
169 ast_str_make_space(&buf, strlen(query->sql_write) * 2);
171 /* Parse our arguments */
172 t = value ? ast_strdupa(value) : "";
175 ast_log(LOG_ERROR, "Out of memory\n");
176 AST_RWLIST_UNLOCK(&queries);
178 ast_autoservice_stop(chan);
180 ast_channel_free(chan);
185 AST_STANDARD_APP_ARGS(args, s);
186 for (i = 0; i < args.argc; i++) {
187 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
188 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
191 /* Parse values, just like arguments */
192 AST_STANDARD_APP_ARGS(values, t);
193 for (i = 0; i < values.argc; i++) {
194 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
195 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
198 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
199 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
201 pbx_substitute_variables_helper(chan, query->sql_write, buf->str, buf->len - 1);
203 /* Restore prior values */
204 for (i = 0; i < args.argc; i++) {
205 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
206 pbx_builtin_setvar_helper(chan, varname, NULL);
209 for (i = 0; i < values.argc; i++) {
210 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
211 pbx_builtin_setvar_helper(chan, varname, NULL);
213 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
215 AST_RWLIST_UNLOCK(&queries);
217 for (dsn = 0; dsn < 5; dsn++) {
218 if (!ast_strlen_zero(query->writehandle[dsn])) {
219 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
221 stmt = ast_odbc_direct_execute(obj, generic_execute, buf);
229 SQLRowCount(stmt, &rows);
232 /* Output the affected rows, for all cases. In the event of failure, we
233 * flag this as -1 rows. Note that this is different from 0 affected rows
234 * which would be the case if we succeeded in our query, but the values did
236 snprintf(varname, sizeof(varname), "%d", (int)rows);
237 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
240 SQLCloseCursor(stmt);
241 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
244 ast_odbc_release_obj(obj);
247 ast_autoservice_stop(chan);
249 ast_channel_free(chan);
255 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
257 struct odbc_obj *obj = NULL;
258 struct acf_odbc_query *query;
259 char varname[15], colnames[2048] = "", rowcount[12] = "-1";
260 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
261 AST_DECLARE_APP_ARGS(args,
262 AST_APP_ARG(field)[100];
264 SQLHSTMT stmt = NULL;
265 SQLSMALLINT colcount=0;
267 SQLSMALLINT collength;
268 struct odbc_datastore *resultset = NULL;
269 struct odbc_datastore_row *row = NULL;
270 struct ast_str *sql = ast_str_create(16);
276 AST_RWLIST_RDLOCK(&queries);
277 AST_RWLIST_TRAVERSE(&queries, query, list) {
278 if (!strcmp(query->acf->name, cmd)) {
284 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
285 AST_RWLIST_UNLOCK(&queries);
286 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
292 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
297 ast_autoservice_start(chan);
299 AST_STANDARD_APP_ARGS(args, s);
300 for (x = 0; x < args.argc; x++) {
301 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
302 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
305 ast_str_make_space(&sql, strlen(query->sql_read) * 2);
306 pbx_substitute_variables_helper(chan, query->sql_read, sql->str, sql->len - 1);
308 /* Restore prior values */
309 for (x = 0; x < args.argc; x++) {
310 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
311 pbx_builtin_setvar_helper(chan, varname, NULL);
314 /* Save these flags, so we can release the lock */
315 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
316 if (ast_test_flag(query, OPT_MULTIROW)) {
317 resultset = ast_calloc(1, sizeof(*resultset));
318 AST_LIST_HEAD_INIT(resultset);
320 rowlimit = query->rowlimit;
324 AST_RWLIST_UNLOCK(&queries);
326 for (dsn = 0; dsn < 5; dsn++) {
327 if (!ast_strlen_zero(query->readhandle[dsn])) {
328 obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
330 stmt = ast_odbc_direct_execute(obj, generic_execute, sql->str);
337 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql->str);
339 ast_odbc_release_obj(obj);
340 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
342 ast_autoservice_stop(chan);
344 ast_channel_free(chan);
349 res = SQLNumResultCols(stmt, &colcount);
350 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
351 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql->str);
352 SQLCloseCursor(stmt);
353 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
354 ast_odbc_release_obj(obj);
355 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
357 ast_autoservice_stop(chan);
359 ast_channel_free(chan);
364 res = SQLFetch(stmt);
365 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
367 if (res == SQL_NO_DATA) {
368 ast_verb(4, "Found no rows [%s]\n", sql->str);
370 ast_copy_string(rowcount, "0", sizeof(rowcount));
372 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
374 SQLCloseCursor(stmt);
375 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
376 ast_odbc_release_obj(obj);
377 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
379 ast_autoservice_stop(chan);
381 ast_channel_free(chan);
386 for (y = 0; y < rowlimit; y++) {
388 for (x = 0; x < colcount; x++) {
396 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
397 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
398 snprintf(colname, sizeof(colname), "field%d", x);
401 if (!ast_strlen_zero(colnames))
402 strncat(colnames, ",", sizeof(colnames) - strlen(colnames) - 1);
403 namelen = strlen(colnames);
405 /* Copy data, encoding '\' and ',' for the argument parser */
406 for (i = 0; i < sizeof(colname); i++) {
407 if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
408 colnames[namelen++] = '\\';
410 colnames[namelen++] = colname[i];
412 if (namelen >= sizeof(colnames) - 2) {
413 colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0';
417 if (colname[i] == '\0')
422 void *tmp = ast_realloc(resultset, sizeof(*resultset) + strlen(colnames) + 1);
424 ast_log(LOG_ERROR, "No space for a new resultset?\n");
426 SQLCloseCursor(stmt);
427 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
428 ast_odbc_release_obj(obj);
429 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
431 ast_autoservice_stop(chan);
433 ast_channel_free(chan);
438 strcpy((char *)resultset + sizeof(*resultset), colnames);
442 buflen = strlen(buf);
443 res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
444 if (indicator == SQL_NULL_DATA) {
449 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
450 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql->str);
455 /* Copy data, encoding '\' and ',' for the argument parser */
456 for (i = 0; i < sizeof(coldata); i++) {
457 if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
458 buf[buflen++] = '\\';
460 buf[buflen++] = coldata[i];
462 if (buflen >= len - 2)
465 if (coldata[i] == '\0')
469 buf[buflen - 1] = ',';
472 /* Trim trailing comma */
473 buf[buflen - 1] = '\0';
476 row = ast_calloc(1, sizeof(*row) + buflen);
478 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
481 strcpy((char *)row + sizeof(*row), buf);
482 AST_LIST_INSERT_TAIL(resultset, row, list);
485 res = SQLFetch(stmt);
486 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
487 if (res != SQL_NO_DATA)
488 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
496 snprintf(rowcount, sizeof(rowcount), "%d", y);
497 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
498 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
501 struct ast_datastore *odbc_store;
502 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
503 snprintf(buf, len, "%d", uid);
504 odbc_store = ast_datastore_alloc(&odbc_info, buf);
506 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
507 odbc_datastore_free(resultset);
508 SQLCloseCursor(stmt);
509 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
510 ast_odbc_release_obj(obj);
512 ast_autoservice_stop(chan);
514 ast_channel_free(chan);
518 odbc_store->data = resultset;
519 ast_channel_datastore_add(chan, odbc_store);
521 SQLCloseCursor(stmt);
522 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
523 ast_odbc_release_obj(obj);
525 ast_autoservice_stop(chan);
527 ast_channel_free(chan);
532 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
536 for (; *data && out - buf < len; data++) {
548 static struct ast_custom_function escape_function = {
550 .synopsis = "Escapes single ticks for use in SQL statements",
551 .syntax = "SQL_ESC(<string>)",
553 "Used in SQL templates to escape data which may contain single ticks (') which\n"
554 "are otherwise used to delimit data. For example:\n"
555 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
560 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
562 struct ast_datastore *store;
563 struct odbc_datastore *resultset;
564 struct odbc_datastore_row *row;
565 store = ast_channel_datastore_find(chan, &odbc_info, data);
569 resultset = store->data;
570 AST_LIST_LOCK(resultset);
571 row = AST_LIST_REMOVE_HEAD(resultset, list);
572 AST_LIST_UNLOCK(resultset);
574 /* Cleanup datastore */
575 ast_channel_datastore_remove(chan, store);
576 ast_datastore_free(store);
579 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
580 ast_copy_string(buf, row->data, len);
585 static struct ast_custom_function fetch_function = {
586 .name = "ODBC_FETCH",
587 .synopsis = "Fetch a row from a multirow query",
588 .syntax = "ODBC_FETCH(<result-id>)",
590 "For queries which are marked as mode=multirow, the original query returns a\n"
591 "result-id from which results may be fetched. This function implements the\n"
592 "actual fetch of the results.\n",
597 static char *app_odbcfinish = "ODBCFinish";
598 static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
599 static char *desc_odbcfinish =
600 "ODBCFinish(<result-id>)\n"
601 " Clears any remaining rows of the specified resultset\n";
604 static int exec_odbcfinish(struct ast_channel *chan, void *data)
606 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
607 if (!store) /* Already freed; no big deal. */
609 ast_channel_datastore_remove(chan, store);
610 ast_datastore_free(store);
614 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
623 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
627 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
628 char *tmp2 = ast_strdupa(tmp);
629 AST_DECLARE_APP_ARGS(write,
632 AST_STANDARD_APP_ARGS(write, tmp2);
633 for (i = 0; i < 5; i++) {
634 if (!ast_strlen_zero(write.dsn[i]))
635 ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i]));
639 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
640 char *tmp2 = ast_strdupa(tmp);
641 AST_DECLARE_APP_ARGS(read,
644 AST_STANDARD_APP_ARGS(read, tmp2);
645 for (i = 0; i < 5; i++) {
646 if (!ast_strlen_zero(read.dsn[i]))
647 ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i]));
650 /* If no separate readhandle, then use the writehandle for reading */
651 for (i = 0; i < 5; i++) {
652 if (!ast_strlen_zero((*query)->writehandle[i]))
653 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
657 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
658 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
659 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
660 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
661 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
664 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
667 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
671 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
672 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
673 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
674 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
675 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
678 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
681 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
685 /* Allow escaping of embedded commas in fields to be turned off */
686 ast_set_flag((*query), OPT_ESCAPECOMMAS);
687 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
689 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
692 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
693 if (strcasecmp(tmp, "multirow") == 0)
694 ast_set_flag((*query), OPT_MULTIROW);
695 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
696 sscanf(tmp, "%d", &((*query)->rowlimit));
699 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
700 if (! (*query)->acf) {
706 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
707 asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
709 asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
712 if (!((*query)->acf->name)) {
713 ast_free((*query)->acf);
719 asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
721 if (!((*query)->acf->syntax)) {
722 ast_free((char *)(*query)->acf->name);
723 ast_free((*query)->acf);
729 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
730 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
731 asprintf((char **)&((*query)->acf->desc),
732 "Runs the following query, as defined in func_odbc.conf, performing\n"
733 "substitution of the arguments into the query as specified by ${ARG1},\n"
734 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
735 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
736 "\nRead:\n%s\n\nWrite:\n%s\n",
738 (*query)->sql_write);
739 } else if (!ast_strlen_zero((*query)->sql_read)) {
740 asprintf((char **)&((*query)->acf->desc),
741 "Runs the following query, as defined in func_odbc.conf, performing\n"
742 "substitution of the arguments into the query as specified by ${ARG1},\n"
743 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
745 } else if (!ast_strlen_zero((*query)->sql_write)) {
746 asprintf((char **)&((*query)->acf->desc),
747 "Runs the following query, as defined in func_odbc.conf, performing\n"
748 "substitution of the arguments into the query as specified by ${ARG1},\n"
749 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
750 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
751 "This function may only be set.\nSQL:\n%s\n",
752 (*query)->sql_write);
754 ast_free((char *)(*query)->acf->syntax);
755 ast_free((char *)(*query)->acf->name);
756 ast_free((*query)->acf);
758 ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute. Ignoring.\n", catg);
762 if (! ((*query)->acf->desc)) {
763 ast_free((char *)(*query)->acf->syntax);
764 ast_free((char *)(*query)->acf->name);
765 ast_free((*query)->acf);
771 if (ast_strlen_zero((*query)->sql_read)) {
772 (*query)->acf->read = NULL;
774 (*query)->acf->read = acf_odbc_read;
777 if (ast_strlen_zero((*query)->sql_write)) {
778 (*query)->acf->write = NULL;
780 (*query)->acf->write = acf_odbc_write;
786 static int free_acf_query(struct acf_odbc_query *query)
790 if (query->acf->name)
791 ast_free((char *)query->acf->name);
792 if (query->acf->syntax)
793 ast_free((char *)query->acf->syntax);
794 if (query->acf->desc)
795 ast_free((char *)query->acf->desc);
796 ast_free(query->acf);
803 static int load_module(void)
806 struct ast_config *cfg;
808 struct ast_flags config_flags = { 0 };
810 res |= ast_custom_function_register(&fetch_function);
811 res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
812 AST_RWLIST_WRLOCK(&queries);
814 cfg = ast_config_load(config, config_flags);
816 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
817 AST_RWLIST_UNLOCK(&queries);
818 return AST_MODULE_LOAD_DECLINE;
821 for (catg = ast_category_browse(cfg, NULL);
823 catg = ast_category_browse(cfg, catg)) {
824 struct acf_odbc_query *query = NULL;
827 if ((err = init_acf_query(cfg, catg, &query))) {
829 ast_log(LOG_ERROR, "Out of memory\n");
830 else if (err == EINVAL)
831 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
833 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
835 AST_RWLIST_INSERT_HEAD(&queries, query, list);
836 ast_custom_function_register(query->acf);
840 ast_config_destroy(cfg);
841 res |= ast_custom_function_register(&escape_function);
843 AST_RWLIST_UNLOCK(&queries);
847 static int unload_module(void)
849 struct acf_odbc_query *query;
852 AST_RWLIST_WRLOCK(&queries);
853 while (!AST_RWLIST_EMPTY(&queries)) {
854 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
855 ast_custom_function_unregister(query->acf);
856 free_acf_query(query);
859 res |= ast_custom_function_unregister(&escape_function);
860 res |= ast_custom_function_unregister(&fetch_function);
861 res |= ast_unregister_application(app_odbcfinish);
863 /* Allow any threads waiting for this lock to pass (avoids a race) */
864 AST_RWLIST_UNLOCK(&queries);
866 AST_RWLIST_WRLOCK(&queries);
868 AST_RWLIST_UNLOCK(&queries);
872 static int reload(void)
875 struct ast_config *cfg;
876 struct acf_odbc_query *oldquery;
878 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
880 cfg = ast_config_load(config, config_flags);
881 if (cfg == CONFIG_STATUS_FILEUNCHANGED)
884 AST_RWLIST_WRLOCK(&queries);
886 while (!AST_RWLIST_EMPTY(&queries)) {
887 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
888 ast_custom_function_unregister(oldquery->acf);
889 free_acf_query(oldquery);
893 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
897 for (catg = ast_category_browse(cfg, NULL);
899 catg = ast_category_browse(cfg, catg)) {
900 struct acf_odbc_query *query = NULL;
902 if (init_acf_query(cfg, catg, &query)) {
903 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
905 AST_RWLIST_INSERT_HEAD(&queries, query, list);
906 ast_custom_function_register(query->acf);
910 ast_config_destroy(cfg);
912 AST_RWLIST_UNLOCK(&queries);
916 /* XXX need to revise usecount - set if query_lock is set */
918 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
920 .unload = unload_module,