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