set standard properties on all non-binary files
[asterisk/asterisk.git] / funcs / func_odbc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (c) 2005 Tilghman Lesher
5  *
6  * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
7  *
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.
13  *
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.
17  */
18
19 /*!
20  * \file
21  *
22  * \brief ODBC lookups
23  *
24  * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
25  */
26
27 #include <sys/types.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include "asterisk/module.h"
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/options.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/config.h"
45 #include "asterisk/res_odbc.h"
46
47 static char *tdesc = "ODBC lookups";
48
49 static char *config = "func_odbc.conf";
50
51 struct acf_odbc_query {
52         char name[30];
53         char dsn[30];
54         char sql_read[512];
55         char sql_write[512];
56         struct ast_custom_function *acf;
57         unsigned int deleteme:1;
58         struct acf_odbc_query *next;
59 };
60
61 static struct acf_odbc_query *queries = NULL;
62 AST_MUTEX_DEFINE_STATIC(query_lock);
63
64 #ifdef NEEDTRACE
65 static void acf_odbc_error(SQLHSTMT stmt, int res)
66 {
67         char state[10] = "", diagnostic[256] = "";
68         SQLINTEGER nativeerror = 0;
69         SQLSMALLINT diagbytes = 0;
70         SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
71         ast_log(LOG_WARNING, "SQL return value %d: error %s: %s (len %d)\n", res, state, diagnostic, diagbytes);
72 }
73 #endif
74
75 /*
76  * Master control routine
77  */
78 static void acf_odbc_write(struct ast_channel *chan, char *cmd, char *data, const char *value)
79 {
80         odbc_obj *obj;
81         struct acf_odbc_query *query;
82         char *s, *t, *arg, buf[512]="", varname[15];
83         int res, argcount=0, valcount=0, i, retry=0;
84         struct ast_channel *ast;
85         SQLHSTMT stmt;
86         SQLINTEGER nativeerror=0, numfields=0, rows=0;
87         SQLSMALLINT diagbytes=0;
88         unsigned char state[10], diagnostic[256];
89 #ifdef NEEDTRACE
90         SQLINTEGER enable = 1;
91         char *tracefile = "/tmp/odbc.trace";
92 #endif
93
94         ast_mutex_lock(&query_lock);
95         for (query=queries; query; query = query->next) {
96                 if (!strcasecmp(query->name, cmd + 5)) {
97                         break;
98                 }
99         }
100
101         if (!query) {
102                 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
103                 ast_mutex_unlock(&query_lock);
104                 return;
105         }
106
107         obj = fetch_odbc_obj(query->dsn, 0);
108
109         if (!obj) {
110                 ast_log(LOG_ERROR, "No such DSN registered: %s (check res_odbc.conf)\n", query->dsn);
111                 ast_mutex_unlock(&query_lock);
112                 return;
113         }
114
115         /* Parse our arguments */
116         s = ast_strdupa(data);
117         if (value) {
118                 t = ast_strdupa(value);
119         } else {
120                 t = "";
121         }
122
123         if (!s || !t) {
124                 ast_log(LOG_ERROR, "Out of memory\n");
125                 ast_mutex_unlock(&query_lock);
126                 return;
127         }
128
129         /* XXX You might be tempted to change this section into using
130          * pbx_builtin_pushvar_helper().  However, note that if you try
131          * to set a NULL (like for VALUE), then nothing gets set, and the
132          * value doesn't get masked out.  Even worse, when you subsequently
133          * try to remove the value you just set, you'll wind up unsetting
134          * the previous value (which is wholly undesireable).  Hence, this
135          * has to remain the way it is done here. XXX
136          */
137
138         /* Save old arguments as variables in a fake channel */
139         ast = ast_channel_alloc(0);
140         while ((arg = strsep(&s, "|"))) {
141                 argcount++;
142                 snprintf(varname, sizeof(varname), "ARG%d", argcount);
143                 pbx_builtin_setvar_helper(ast, varname, pbx_builtin_getvar_helper(chan, varname));
144                 pbx_builtin_setvar_helper(chan, varname, arg);
145         }
146
147         /* Parse values, just like arguments */
148         while ((arg = strsep(&t, "|"))) {
149                 valcount++;
150                 snprintf(varname, sizeof(varname), "VAL%d", valcount);
151                 pbx_builtin_setvar_helper(ast, varname, pbx_builtin_getvar_helper(chan, varname));
152                 pbx_builtin_setvar_helper(chan, varname, arg);
153         }
154
155         /* Additionally set the value as a whole */
156         /* Note that pbx_builtin_setvar_helper will quite happily take a NULL for the 3rd argument */
157         pbx_builtin_setvar_helper(ast, "VALUE", pbx_builtin_getvar_helper(chan, "VALUE"));
158         pbx_builtin_setvar_helper(chan, "VALUE", value);
159
160         pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
161
162         /* Restore prior values */
163         for (i=1; i<=argcount; i++) {
164                 snprintf(varname, sizeof(varname), "ARG%d", argcount);
165                 pbx_builtin_setvar_helper(chan, varname, pbx_builtin_getvar_helper(ast, varname));
166         }
167
168         for (i=1; i<=valcount; i++) {
169                 snprintf(varname, sizeof(varname), "VAL%d", argcount);
170                 pbx_builtin_setvar_helper(chan, varname, pbx_builtin_getvar_helper(ast, varname));
171         }
172         pbx_builtin_setvar_helper(chan, "VALUE", pbx_builtin_getvar_helper(ast, "VALUE"));
173
174         ast_channel_free(ast);
175         ast_mutex_unlock(&query_lock);
176
177 retry_write:
178 #ifdef NEEDTRACE
179         SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
180         SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
181 #endif
182
183         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
184         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
185                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
186                 pbx_builtin_setvar_helper(chan, "ODBCROWS", "-1");
187                 return;
188         }
189
190         res = SQLPrepare(stmt, (unsigned char *)buf, SQL_NTS);
191         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
192                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", buf);
193                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
194                 pbx_builtin_setvar_helper(chan, "ODBCROWS", "-1");
195                 return;
196         }
197
198         res = SQLExecute(stmt);
199         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
200                 if (res == SQL_ERROR) {
201                         SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
202                         for (i = 0; i <= numfields; i++) {
203                                 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
204                                 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
205                                 if (i > 10) {
206                                         ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
207                                         break;
208                                 }
209                         }
210                 }
211                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
212                 odbc_obj_disconnect(obj);
213                 /* All handles are now invalid (after a disconnect), so we gotta redo all handles */
214                 odbc_obj_connect(obj);
215                 if (!retry) {
216                         retry = 1;
217                         goto retry_write;
218                 }
219                 rows = -1;
220         } else {
221                 /* Rows affected */
222                 SQLRowCount(stmt, &rows);
223         }
224
225         /* Output the affected rows, for all cases.  In the event of failure, we
226          * flag this as -1 rows.  Note that this is different from 0 affected rows
227          * which would be the case if we succeeded in our query, but the values did
228          * not change. */
229         snprintf(varname, sizeof(varname), "%d", (int)rows);
230         pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
231
232         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
233                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", buf);
234         }
235
236         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
237 }
238
239 static char *acf_odbc_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
240 {
241         odbc_obj *obj;
242         struct acf_odbc_query *query;
243         char *s, *arg, sql[512] = "", varname[15];
244         int count=0, res, x;
245         SQLHSTMT stmt;
246         SQLSMALLINT colcount=0;
247         SQLINTEGER indicator;
248 #ifdef NEEDTRACE
249         SQLINTEGER enable = 1;
250         char *tracefile = "/tmp/odbc.trace";
251 #endif
252
253         ast_mutex_lock(&query_lock);
254         for (query=queries; query; query = query->next) {
255                 if (!strcasecmp(query->name, cmd + 5)) {
256                         break;
257                 }
258         }
259
260         if (!query) {
261                 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
262                 ast_mutex_unlock(&query_lock);
263                 return "";
264         }
265
266         obj = fetch_odbc_obj(query->dsn, 0);
267
268         if (!obj) {
269                 ast_log(LOG_ERROR, "No such DSN registered: %s (check res_odbc.conf)\n", query->dsn);
270                 ast_mutex_unlock(&query_lock);
271                 return "";
272         }
273
274 #ifdef NEEDTRACE
275         SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
276         SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
277 #endif
278
279         /* Parse our arguments */
280         if (!(s = ast_strdupa(data))) {
281                 ast_mutex_unlock(&query_lock);
282                 return "";
283         }
284
285         while ((arg = strsep(&s, "|"))) {
286                 count++;
287                 snprintf(varname, sizeof(varname), "ARG%d", count);
288                 /* arg is by definition non-NULL, so this works, here */
289                 pbx_builtin_pushvar_helper(chan, varname, arg);
290         }
291
292         pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
293
294         /* Restore prior values */
295         for (x = 1; x <= count; x++) {
296                 snprintf(varname, sizeof(varname), "ARG%d", x);
297                 pbx_builtin_setvar_helper(chan, varname, NULL);
298         }
299
300         ast_mutex_unlock(&query_lock);
301
302         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
303         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
304                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
305                 return "";
306         }
307
308         res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
309         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
310                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
311                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
312                 return "";
313         }
314
315         res = odbc_smart_execute(obj, stmt);
316         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
317                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
318                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
319                 return "";
320         }
321
322         res = SQLNumResultCols(stmt, &colcount);
323         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
324                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
325                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
326                 return "";
327         }
328
329         memset(buf, 0, len);
330
331         res = SQLFetch(stmt);
332         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
333                 if (res == SQL_NO_DATA) {
334                         if (option_verbose > 3) {
335                                 ast_verbose(VERBOSE_PREFIX_4 "Found no rows [%s]\n", sql);
336                         }
337                 } else if (option_verbose > 3) {
338                         ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
339                 }
340                 goto acf_out;
341         }
342
343         for (x=0; x<colcount; x++) {
344                 int buflen, coldatalen;
345                 char coldata[256];
346
347                 buflen = strlen(buf);
348                 res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
349                 if (indicator == SQL_NULL_DATA) {
350                         coldata[0] = '\0';
351                         res = SQL_SUCCESS;
352                 }
353
354                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
355                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
356                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
357                         return "";
358                 }
359
360                 strncat(buf + buflen, coldata, len - buflen);
361                 coldatalen = strlen(coldata);
362                 strncat(buf + buflen + coldatalen, ",", len - buflen - coldatalen);
363         }
364         /* Trim trailing comma */
365         buf[strlen(buf) - 1] = '\0';
366
367 acf_out:
368         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
369         return buf;
370 }
371
372 static char *acf_escape(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
373 {
374         char *in, *out = buf;
375         for (in = data; *in && out - buf < len; in++) {
376                 if (*in == '\'') {
377                         *out = '\'';
378                         out++;
379                 }
380                 *out = *in;
381                 out++;
382         }
383         *out = '\0';
384         return buf;
385 }
386
387 static struct ast_custom_function escape_function = {
388         .name = "SQL_ESC",
389         .synopsis = "Escapes single ticks for use in SQL statements",
390         .syntax = "SQL_ESC(<string>)",
391         .desc =
392 "Used in SQL templates to escape data which may contain single ticks (') which\n"
393 "are otherwise used to delimit data.  For example:\n"
394 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
395         .read = acf_escape,
396         .write = NULL,
397 };
398
399
400
401 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
402 {
403         char *tmp;
404
405         if (!cfg || !catg) {
406                 return -1;
407         }
408
409         *query = calloc(1, sizeof(struct acf_odbc_query));
410         if (! (*query))
411                 return -1;
412
413         ast_copy_string((*query)->name, catg, sizeof((*query)->name));
414
415         if ((tmp = ast_variable_retrieve(cfg, catg, "dsn"))) {
416                 ast_copy_string((*query)->dsn, tmp, sizeof((*query)->dsn));
417         } else {
418                 return -1;
419         }
420
421         if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
422                 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
423         }
424
425         if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
426                 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
427         }
428
429         (*query)->acf = calloc(1, sizeof(struct ast_custom_function));
430         if ((*query)->acf) {
431                 asprintf(&((*query)->acf->name), "ODBC_%s", catg);
432                 asprintf(&((*query)->acf->syntax), "ODBC_%s(<arg1>[...[,<argN>]])", catg);
433                 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
434                 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
435                         asprintf(&((*query)->acf->desc),
436                                                 "Runs the following query, as defined in func_odbc.conf, performing\n"
437                                                 "substitution of the arguments into the query as specified by ${ARG1},\n"
438                                                 "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
439                                                 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
440                                                 "\nRead:\n%s\n\nWrite:\n%s\n",
441                                                 (*query)->sql_read,
442                                                 (*query)->sql_write);
443                 } else if (!ast_strlen_zero((*query)->sql_read)) {
444                         asprintf(&((*query)->acf->desc),
445                                                 "Runs the following query, as defined in func_odbc.conf, performing\n"
446                                                 "substitution of the arguments into the query as specified by ${ARG1},\n"
447                                                 "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
448                                                 (*query)->sql_read);
449                 } else if (!ast_strlen_zero((*query)->sql_write)) {
450                         asprintf(&((*query)->acf->desc),
451                                                 "Runs the following query, as defined in func_odbc.conf, performing\n"
452                                                 "substitution of the arguments into the query as specified by ${ARG1},\n"
453                                                 "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
454                                                 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
455                                                 "This function may only be set.\nSQL:\n%s\n",
456                                                 (*query)->sql_write);
457                 }
458
459                 if (ast_strlen_zero((*query)->sql_read)) {
460                         (*query)->acf->read = NULL;
461                 } else {
462                         (*query)->acf->read = acf_odbc_read;
463                 }
464
465                 if (ast_strlen_zero((*query)->sql_write)) {
466                         (*query)->acf->write = NULL;
467                 } else {
468                         (*query)->acf->write = acf_odbc_write;
469                 }
470
471                 if (! (*query)->acf->name || ! (*query)->acf->syntax || ! (*query)->acf->desc) {
472                         return -1;
473                 }
474         } else {
475                 return -1;
476         }
477         return 0;
478 }
479
480 static int free_acf_query(struct acf_odbc_query *query)
481 {
482         if (query) {
483                 if (query->acf) {
484                         if (query->acf->name)
485                                 free(query->acf->name);
486                         if (query->acf->syntax)
487                                 free(query->acf->syntax);
488                         if (query->acf->desc)
489                                 free(query->acf->desc);
490                         free(query->acf);
491                 }
492                 free(query);
493         }
494         return 0;
495 }
496
497 static int odbc_load_module(void)
498 {
499         int res = 0;
500         struct ast_config *cfg;
501         char *catg;
502
503         ast_mutex_lock(&query_lock);
504
505         cfg = ast_config_load(config);
506         if (!cfg) {
507                 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
508                 goto out;
509         }
510
511         for (catg = ast_category_browse(cfg, NULL);
512              catg;
513              catg = ast_category_browse(cfg, catg)) {
514                 struct acf_odbc_query *query=NULL;
515
516                 if (init_acf_query(cfg, catg, &query)) {
517                         ast_log(LOG_ERROR, "Out of memory\n");
518                         free_acf_query(query);
519                 } else {
520                         query->next = queries;
521                         queries = query;
522                         ast_custom_function_register(query->acf);
523                 }
524         }
525
526         ast_config_destroy(cfg);
527         ast_custom_function_register(&escape_function);
528 out:
529         ast_mutex_unlock(&query_lock);
530         return res;
531 }
532
533 static int odbc_unload_module(void)
534 {
535         struct acf_odbc_query *query, *lastquery = NULL;
536
537         ast_mutex_lock(&query_lock);
538         for (query = queries; query; query = query->next) {
539                 if (lastquery)
540                         free_acf_query(lastquery);
541                 if (ast_custom_function_unregister(query->acf)) {
542                         ast_log(LOG_ERROR, "Cannot unregister function '%s'?\n", query->acf->name);
543                         /* Keep state valid */
544                         queries = query;
545                         ast_mutex_unlock(&query_lock);
546                         return -1;
547                 } else {
548                         /* If anything is waiting on this lock, this will let it pass (avoids a race) */
549                         ast_mutex_unlock(&query_lock);
550                         ast_mutex_lock(&query_lock);
551                         lastquery = query;
552                 }
553         }
554         if (lastquery)
555                 free(lastquery);
556         queries = NULL;
557
558         ast_custom_function_unregister(&escape_function);
559
560         ast_mutex_unlock(&query_lock);
561         return 0;
562 }
563
564 int reload(void)
565 {
566         int res = 0;
567         struct ast_config *cfg;
568         struct acf_odbc_query *q, *prevq = NULL, *qdel = NULL;
569         char *catg;
570
571         ast_mutex_lock(&query_lock);
572
573         for (q = queries; q; q = q->next) {
574                 q->deleteme = 1;
575         }
576
577         cfg = ast_config_load(config);
578         if (!cfg) {
579                 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
580                 goto reload_out;
581         }
582
583         for (catg = ast_category_browse(cfg, NULL);
584              catg;
585              catg = ast_category_browse(cfg, catg)) {
586                 struct acf_odbc_query *query = NULL;
587
588                 /* We do this piecemeal, so that we stay in a consistent state, if there's ever an error */
589                 for (q = queries, prevq=NULL; q; prevq=q, q = q->next) {
590                         if (!strcasecmp(catg, q->name)) {
591                                 break;
592                         }
593                 }
594
595                 if (init_acf_query(cfg, catg, &query)) {
596                         ast_log(LOG_ERROR, "Cannot initialize query ODBC_%s\n", catg);
597                         free_acf_query(query);
598                 } else {
599                         if (q) {
600                                 /* Replacement */
601                                 if (ast_custom_function_unregister(q->acf)) {
602                                         ast_log(LOG_ERROR, "Cannot reload query %s\n", query->acf->name);
603                                         free_acf_query(query);
604                                 } else {
605                                         ast_custom_function_register(query->acf);
606                                         /* Add it to the list */
607                                         if (prevq)
608                                                 prevq->next = query;
609                                         else
610                                                 queries = query;
611                                         query->next = q->next;
612                                         /* Get rid of the old record */
613                                         free_acf_query(q);
614                                 }
615                         } else {
616                                 /* New */
617                                 query->next = queries;
618                                 queries = query;
619                                 ast_custom_function_register(query->acf);
620                         }
621                 }
622         }
623
624         /* Any remaining sets will now be destroyed */
625         for (q = queries; q; q = q->next) {
626                 if (qdel) {
627                         free_acf_query(qdel);
628                         qdel = NULL;
629                 }
630
631                 if (q->deleteme) {
632                         if (ast_custom_function_unregister(q->acf)) {
633                                 ast_log(LOG_ERROR, "Cannot unregister function?  Refusing to make 'ODBC_%s' go away.\n", q->name);
634                         } else {
635                                 /* If anything is waiting on the lock to execute, this will dispose of it (without a race) */
636                                 ast_mutex_unlock(&query_lock);
637                                 ast_mutex_lock(&query_lock);
638
639                                 if (prevq) {
640                                         prevq->next = q->next;
641                                 } else {
642                                         queries = q->next;
643                                 }
644                                 qdel = q;
645                         }
646                 } else {
647                         prevq = q;
648                 }
649         }
650         if (qdel)
651                 free_acf_query(qdel);
652
653         ast_config_destroy(cfg);
654 reload_out:
655         ast_mutex_unlock(&query_lock);
656         return res;
657 }
658
659 int unload_module(void)
660 {
661         return odbc_unload_module();
662 }
663
664 int load_module(void)
665 {
666         return odbc_load_module();
667 }
668
669 char *description(void)
670 {
671         return tdesc;
672 }
673
674 int usecount(void)
675 {
676         if (! ast_mutex_trylock(&query_lock)) {
677                 ast_mutex_unlock(&query_lock);
678                 return 0;
679         } else {
680                 return 1;
681         }
682 }
683
684 char *key()
685 {
686         return ASTERISK_GPL_KEY;
687 }