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