Continuation of issue 9968 (revision 69081). This should be the last one.
[asterisk/asterisk.git] / res / res_odbc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * res_odbc.c <ODBC resource manager>
9  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief ODBC resource manager
25  * 
26  * \author Mark Spencer <markster@digium.com>
27  * \author Anthony Minessale II <anthmct@yahoo.com>
28  *
29  * \arg See also: \ref cdr_odbc
30  */
31
32 /*** MODULEINFO
33         <depend>unixodbc</depend>
34  ***/
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44
45 #include "asterisk/file.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/config.h"
49 #include "asterisk/options.h"
50 #include "asterisk/pbx.h"
51 #include "asterisk/module.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/lock.h"
54 #include "asterisk/res_odbc.h"
55
56 struct odbc_class
57 {
58         AST_LIST_ENTRY(odbc_class) list;
59         char name[80];
60         char dsn[80];
61         char username[80];
62         char password[80];
63         char sanitysql[256];
64         SQLHENV env;
65         unsigned int haspool:1;         /* Boolean - TDS databases need this */
66         unsigned int limit:10;          /* Gives a limit of 1023 maximum */
67         unsigned int count:10;          /* Running count of pooled connections */
68         unsigned int delme:1;                   /* Purge the class */
69         AST_LIST_HEAD(, odbc_obj) odbc_obj;
70 };
71
72 AST_LIST_HEAD_STATIC(odbc_list, odbc_class);
73
74 static odbc_status odbc_obj_connect(struct odbc_obj *obj);
75 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
76 static int odbc_register_class(struct odbc_class *class, int connect);
77
78
79 SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
80 {
81         int res = 0, i, attempt;
82         SQLINTEGER nativeerror=0, numfields=0;
83         SQLSMALLINT diagbytes=0;
84         unsigned char state[10], diagnostic[256];
85         SQLHSTMT stmt;
86
87         for (attempt = 0; attempt < 2; attempt++) {
88                 /* This prepare callback may do more than just prepare -- it may also
89                  * bind parameters, bind results, etc.  The real key, here, is that
90                  * when we disconnect, all handles become invalid for most databases.
91                  * We must therefore redo everything when we establish a new
92                  * connection. */
93                 stmt = prepare_cb(obj, data);
94
95                 if (stmt) {
96                         res = SQLExecute(stmt);
97                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
98                                 if (res == SQL_ERROR) {
99                                         SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
100                                         for (i = 0; i < numfields; i++) {
101                                                 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
102                                                 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
103                                                 if (i > 10) {
104                                                         ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
105                                                         break;
106                                                 }
107                                         }
108                                 }
109
110                                 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
111                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
112                                 stmt = NULL;
113
114                                 obj->up = 0;
115                                 /*
116                                  * While this isn't the best way to try to correct an error, this won't automatically
117                                  * fail when the statement handle invalidates.
118                                  */
119                                 ast_odbc_sanity_check(obj);
120                                 continue;
121                         }
122                         break;
123                 } else if (attempt == 0)
124                         ast_odbc_sanity_check(obj);
125         }
126
127         return stmt;
128 }
129
130 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) 
131 {
132         int res = 0, i;
133         SQLINTEGER nativeerror=0, numfields=0;
134         SQLSMALLINT diagbytes=0;
135         unsigned char state[10], diagnostic[256];
136
137         res = SQLExecute(stmt);
138         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
139                 if (res == SQL_ERROR) {
140                         SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
141                         for (i = 0; i < numfields; i++) {
142                                 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
143                                 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
144                                 if (i > 10) {
145                                         ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
146                                         break;
147                                 }
148                         }
149                 }
150 #if 0
151                 /* This is a really bad method of trying to correct a dead connection.  It
152                  * only ever really worked with MySQL.  It will not work with any other
153                  * database, since most databases prepare their statements on the server,
154                  * and if you disconnect, you invalidate the statement handle.  Hence, if
155                  * you disconnect, you're going to fail anyway, whether you try to execute
156                  * a second time or not.
157                  */
158                 ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
159                 ast_mutex_lock(&obj->lock);
160                 obj->up = 0;
161                 ast_mutex_unlock(&obj->lock);
162                 odbc_obj_disconnect(obj);
163                 odbc_obj_connect(obj);
164                 res = SQLExecute(stmt);
165 #endif
166         }
167         
168         return res;
169 }
170
171
172 int ast_odbc_sanity_check(struct odbc_obj *obj) 
173 {
174         char *test_sql = "select 1";
175         SQLHSTMT stmt;
176         int res = 0;
177
178         if (obj->parent->sanitysql)
179                 test_sql = obj->parent->sanitysql;
180
181         if (obj->up) {
182                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
183                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
184                         obj->up = 0;
185                 } else {
186                         res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
187                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
188                                 obj->up = 0;
189                         } else {
190                                 res = SQLExecute(stmt);
191                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
192                                         obj->up = 0;
193                                 }
194                         }
195                 }
196                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
197         }
198
199         if (!obj->up) { /* Try to reconnect! */
200                 ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
201                 odbc_obj_disconnect(obj);
202                 odbc_obj_connect(obj);
203         }
204         return obj->up;
205 }
206
207 static int load_odbc_config(void)
208 {
209         static char *cfg = "res_odbc.conf";
210         struct ast_config *config;
211         struct ast_variable *v;
212         char *cat, *dsn, *username, *password, *sanitysql;
213         int enabled, pooling, limit;
214         int connect = 0, res = 0;
215
216         struct odbc_class *new;
217
218         config = ast_config_load(cfg);
219         if (!config) {
220                 ast_log(LOG_WARNING, "Unable to load config file res_odbc.conf\n");
221                 return -1;
222         }
223         for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
224                 if (!strcasecmp(cat, "ENV")) {
225                         for (v = ast_variable_browse(config, cat); v; v = v->next) {
226                                 setenv(v->name, v->value, 1);
227                                 ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
228                         }
229                 } else {
230                         /* Reset all to defaults for each class of odbc connections */
231                         dsn = username = password = sanitysql = NULL;
232                         enabled = 1;
233                         connect = 0;
234                         pooling = 0;
235                         limit = 0;
236                         for (v = ast_variable_browse(config, cat); v; v = v->next) {
237                                 if (!strcasecmp(v->name, "pooling")) {
238                                         if (ast_true(v->value))
239                                                 pooling = 1;
240                                 } else if (!strcasecmp(v->name, "limit")) {
241                                         sscanf(v->value, "%d", &limit);
242                                         if (ast_true(v->value) && !limit) {
243                                                 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
244                                                 limit = 1023;
245                                         } else if (ast_false(v->value)) {
246                                                 ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
247                                                 enabled = 0;
248                                                 break;
249                                         }
250                                 } else if (!strcasecmp(v->name, "enabled")) {
251                                         enabled = ast_true(v->value);
252                                 } else if (!strcasecmp(v->name, "pre-connect")) {
253                                         connect = ast_true(v->value);
254                                 } else if (!strcasecmp(v->name, "dsn")) {
255                                         dsn = v->value;
256                                 } else if (!strcasecmp(v->name, "username")) {
257                                         username = v->value;
258                                 } else if (!strcasecmp(v->name, "password")) {
259                                         password = v->value;
260                                 } else if (!strcasecmp(v->name, "sanitysql")) {
261                                         sanitysql = v->value;
262                                 }
263                         }
264
265                         if (enabled && !ast_strlen_zero(dsn)) {
266                                 new = ast_calloc(1, sizeof(*new));
267
268                                 if (!new) {
269                                         res = -1;
270                                         break;
271                                 }
272
273                                 if (cat)
274                                         ast_copy_string(new->name, cat, sizeof(new->name));
275                                 if (dsn)
276                                         ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
277                                 if (username)
278                                         ast_copy_string(new->username, username, sizeof(new->username));
279                                 if (password)
280                                         ast_copy_string(new->password, password, sizeof(new->password));
281                                 if (sanitysql)
282                                         ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql));
283
284                                 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
285                                 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
286
287                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
288                                         ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
289                                         SQLFreeHandle(SQL_HANDLE_ENV, new->env);
290                                         return res;
291                                 }
292
293                                 if (pooling) {
294                                         new->haspool = pooling;
295                                         if (limit) {
296                                                 new->limit = limit;
297                                         } else {
298                                                 ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
299                                                 new->limit = 5;
300                                         }
301                                 }
302
303                                 odbc_register_class(new, connect);
304                                 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
305                         }
306                 }
307         }
308         ast_config_destroy(config);
309         return res;
310 }
311
312 static int odbc_show_command(int fd, int argc, char **argv)
313 {
314         struct odbc_class *class;
315         struct odbc_obj *current;
316
317         AST_LIST_LOCK(&odbc_list);
318         AST_LIST_TRAVERSE(&odbc_list, class, list) {
319                 if ((argc == 2) || (argc == 3 && !strcmp(argv[2], "all")) || (!strcmp(argv[2], class->name))) {
320                         int count = 0;
321                         ast_cli(fd, "Name: %s\nDSN: %s\n", class->name, class->dsn);
322
323                         if (class->haspool) {
324                                 ast_cli(fd, "Pooled: yes\nLimit: %d\nConnections in use: %d\n", class->limit, class->count);
325
326                                 AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
327                                         ast_cli(fd, "  Connection %d: %s", ++count, current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected");
328                                 }
329                         } else {
330                                 /* Should only ever be one of these */
331                                 AST_LIST_TRAVERSE(&(class->odbc_obj), current, list) {
332                                         ast_cli(fd, "Pooled: no\nConnected: %s\n", current->up && ast_odbc_sanity_check(current) ? "yes" : "no");
333                                 }
334                         }
335
336                                 ast_cli(fd, "\n");
337                 }
338         }
339         AST_LIST_UNLOCK(&odbc_list);
340
341         return 0;
342 }
343
344 static const char show_usage[] =
345 "Usage: odbc show [<class>]\n"
346 "       List settings of a particular ODBC class.\n"
347 "       or, if not specified, all classes.\n";
348
349 static struct ast_cli_entry cli_odbc[] = {
350         { { "odbc", "show", NULL },
351         odbc_show_command, "List ODBC DSN(s)",
352         show_usage },
353 };
354
355 static int odbc_register_class(struct odbc_class *class, int connect)
356 {
357         struct odbc_obj *obj;
358         if (class) {
359                 AST_LIST_LOCK(&odbc_list);
360                 AST_LIST_INSERT_HEAD(&odbc_list, class, list);
361                 AST_LIST_UNLOCK(&odbc_list);
362
363                 if (connect) {
364                         /* Request and release builds a connection */
365                         obj = ast_odbc_request_obj(class->name, 0);
366                         if (obj)
367                                 ast_odbc_release_obj(obj);
368                 }
369
370                 return 0;
371         } else {
372                 ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
373                 return -1;
374         }
375 }
376
377 void ast_odbc_release_obj(struct odbc_obj *obj)
378 {
379         /* For pooled connections, this frees the connection to be
380          * reused.  For non-pooled connections, it does nothing. */
381         obj->used = 0;
382 }
383
384 struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
385 {
386         struct odbc_obj *obj = NULL;
387         struct odbc_class *class;
388
389         AST_LIST_LOCK(&odbc_list);
390         AST_LIST_TRAVERSE(&odbc_list, class, list) {
391                 if (!strcmp(class->name, name))
392                         break;
393         }
394         AST_LIST_UNLOCK(&odbc_list);
395
396         if (!class)
397                 return NULL;
398
399         AST_LIST_LOCK(&class->odbc_obj);
400         if (class->haspool) {
401                 /* Recycle connections before building another */
402                 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
403                         if (! obj->used) {
404                                 obj->used = 1;
405                                 break;
406                         }
407                 }
408
409                 if (!obj && (class->count < class->limit)) {
410                         class->count++;
411                         obj = ast_calloc(1, sizeof(*obj));
412                         if (!obj) {
413                                 AST_LIST_UNLOCK(&class->odbc_obj);
414                                 return NULL;
415                         }
416                         ast_mutex_init(&obj->lock);
417                         obj->parent = class;
418                         odbc_obj_connect(obj);
419                         AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
420                 }
421         } else {
422                 /* Non-pooled connection: multiple modules can use the same connection. */
423                 AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
424                         /* Non-pooled connection: if there is an entry, return it */
425                         break;
426                 }
427
428                 if (!obj) {
429                         /* No entry: build one */
430                         obj = ast_calloc(1, sizeof(*obj));
431                         if (!obj) {
432                                 AST_LIST_UNLOCK(&class->odbc_obj);
433                                 return NULL;
434                         }
435                         ast_mutex_init(&obj->lock);
436                         obj->parent = class;
437                         if (odbc_obj_connect(obj) == ODBC_FAIL) {
438                                 ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
439                                 ast_mutex_destroy(&obj->lock);
440                                 ast_free(obj);
441                                 obj = NULL;
442                         } else {
443                                 AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
444                         }
445                 }
446         }
447         AST_LIST_UNLOCK(&class->odbc_obj);
448
449         if (obj && check) {
450                 ast_odbc_sanity_check(obj);
451         }
452         return obj;
453 }
454
455 static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
456 {
457         int res;
458         ast_mutex_lock(&obj->lock);
459
460         res = SQLDisconnect(obj->con);
461
462         if (res == ODBC_SUCCESS) {
463                 ast_log(LOG_WARNING, "res_odbc: disconnected %d from %s [%s]\n", res, obj->parent->name, obj->parent->dsn);
464         } else {
465                 ast_log(LOG_WARNING, "res_odbc: %s [%s] already disconnected\n",
466                 obj->parent->name, obj->parent->dsn);
467         }
468         obj->up = 0;
469         ast_mutex_unlock(&obj->lock);
470         return ODBC_SUCCESS;
471 }
472
473 static odbc_status odbc_obj_connect(struct odbc_obj *obj)
474 {
475         int res;
476         SQLINTEGER err;
477         short int mlen;
478         unsigned char msg[200], stat[10];
479 #ifdef NEEDTRACE
480         SQLINTEGER enable = 1;
481         char *tracefile = "/tmp/odbc.trace";
482 #endif
483         ast_mutex_lock(&obj->lock);
484
485         res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &obj->con);
486
487         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
488
489                 ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
490                 SQLFreeHandle(SQL_HANDLE_ENV, obj->parent->env);
491
492                 ast_mutex_unlock(&obj->lock);
493                 return ODBC_FAIL;
494         }
495         SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
496 #ifdef NEEDTRACE
497         SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
498         SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
499 #endif
500
501         if (obj->up) {
502                 odbc_obj_disconnect(obj);
503                 ast_log(LOG_NOTICE, "Re-connecting %s\n", obj->parent->name);
504         } else {
505                 ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
506         }
507
508         res = SQLConnect(obj->con,
509                    (SQLCHAR *) obj->parent->dsn, SQL_NTS,
510                    (SQLCHAR *) obj->parent->username, SQL_NTS,
511                    (SQLCHAR *) obj->parent->password, SQL_NTS);
512
513         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
514                 SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, stat, &err, msg, 100, &mlen);
515                 ast_mutex_unlock(&obj->lock);
516                 ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
517                 return ODBC_FAIL;
518         } else {
519                 ast_log(LOG_NOTICE, "res_odbc: Connected to %s [%s]\n", obj->parent->name, obj->parent->dsn);
520                 obj->up = 1;
521         }
522
523         ast_mutex_unlock(&obj->lock);
524         return ODBC_SUCCESS;
525 }
526
527 static int reload(void)
528 {
529         static char *cfg = "res_odbc.conf";
530         struct ast_config *config;
531         struct ast_variable *v;
532         char *cat, *dsn, *username, *password, *sanitysql;
533         int enabled, pooling, limit;
534         int connect = 0, res = 0;
535
536         struct odbc_class *new, *class;
537         struct odbc_obj *current;
538
539         /* First, mark all to be purged */
540         AST_LIST_LOCK(&odbc_list);
541         AST_LIST_TRAVERSE(&odbc_list, class, list) {
542                 class->delme = 1;
543         }
544
545         config = ast_config_load(cfg);
546         if (config) {
547                 for (cat = ast_category_browse(config, NULL); cat; cat=ast_category_browse(config, cat)) {
548                         if (!strcasecmp(cat, "ENV")) {
549                                 for (v = ast_variable_browse(config, cat); v; v = v->next) {
550                                         setenv(v->name, v->value, 1);
551                                         ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value);
552                                 }
553                         } else {
554                                 /* Reset all to defaults for each class of odbc connections */
555                                 dsn = username = password = sanitysql = NULL;
556                                 enabled = 1;
557                                 connect = 0;
558                                 pooling = 0;
559                                 limit = 0;
560                                 for (v = ast_variable_browse(config, cat); v; v = v->next) {
561                                         if (!strcasecmp(v->name, "pooling")) {
562                                                 pooling = 1;
563                                         } else if (!strcasecmp(v->name, "limit")) {
564                                                 sscanf(v->value, "%d", &limit);
565                                                 if (ast_true(v->value) && !limit) {
566                                                         ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Setting limit to 1023 for ODBC class '%s'.\n", v->value, cat);
567                                                         limit = 1023;
568                                                 } else if (ast_false(v->value)) {
569                                                         ast_log(LOG_WARNING, "Limit should be a number, not a boolean: '%s'.  Disabling ODBC class '%s'.\n", v->value, cat);
570                                                         enabled = 0;
571                                                         break;
572                                                 }
573                                         } else if (!strcasecmp(v->name, "enabled")) {
574                                                 enabled = ast_true(v->value);
575                                         } else if (!strcasecmp(v->name, "pre-connect")) {
576                                                 connect = ast_true(v->value);
577                                         } else if (!strcasecmp(v->name, "dsn")) {
578                                                 dsn = v->value;
579                                         } else if (!strcasecmp(v->name, "username")) {
580                                                 username = v->value;
581                                         } else if (!strcasecmp(v->name, "password")) {
582                                                 password = v->value;
583                                         } else if (!strcasecmp(v->name, "sanitysql")) {
584                                                 sanitysql = v->value;
585                                         }
586                                 }
587
588                                 if (enabled && !ast_strlen_zero(dsn)) {
589                                         /* First, check the list to see if it already exists */
590                                         AST_LIST_TRAVERSE(&odbc_list, class, list) {
591                                                 if (!strcmp(class->name, cat)) {
592                                                         class->delme = 0;
593                                                         break;
594                                                 }
595                                         }
596
597                                         if (class) {
598                                                 new = class;
599                                         } else {
600                                                 new = ast_calloc(1, sizeof(*new));
601                                         }
602
603                                         if (!new) {
604                                                 res = -1;
605                                                 break;
606                                         }
607
608                                         if (cat)
609                                                 ast_copy_string(new->name, cat, sizeof(new->name));
610                                         if (dsn)
611                                                 ast_copy_string(new->dsn, dsn, sizeof(new->dsn));
612                                         if (username)
613                                                 ast_copy_string(new->username, username, sizeof(new->username));
614                                         if (password)
615                                                 ast_copy_string(new->password, password, sizeof(new->password));
616                                         if (sanitysql)
617                                                 ast_copy_string(new->sanitysql, sanitysql, sizeof(new->sanitysql));
618
619                                         if (!class) {
620                                                 SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &new->env);
621                                                 res = SQLSetEnvAttr(new->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
622
623                                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
624                                                         ast_log(LOG_WARNING, "res_odbc: Error SetEnv\n");
625                                                         SQLFreeHandle(SQL_HANDLE_ENV, new->env);
626                                                         AST_LIST_UNLOCK(&odbc_list);
627                                                         return res;
628                                                 }
629                                         }
630
631                                         if (pooling) {
632                                                 new->haspool = pooling;
633                                                 if (limit) {
634                                                         new->limit = limit;
635                                                 } else {
636                                                         ast_log(LOG_WARNING, "Pooling without also setting a limit is pointless.  Changing limit from 0 to 5.\n");
637                                                         new->limit = 5;
638                                                 }
639                                         }
640
641                                         if (class) {
642                                                 ast_log(LOG_NOTICE, "Refreshing ODBC class '%s' dsn->[%s]\n", cat, dsn);
643                                         } else {
644                                                 odbc_register_class(new, connect);
645                                                 ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
646                                         }
647                                 }
648                         }
649                 }
650                 ast_config_destroy(config);
651         }
652
653         /* Purge classes that we know can go away (pooled with 0, only) */
654         AST_LIST_TRAVERSE_SAFE_BEGIN(&odbc_list, class, list) {
655                 if (class->delme && class->haspool && class->count == 0) {
656                         AST_LIST_TRAVERSE_SAFE_BEGIN(&(class->odbc_obj), current, list) {
657                                 AST_LIST_REMOVE_CURRENT(&(class->odbc_obj), list);
658                                 odbc_obj_disconnect(current);
659                                 ast_mutex_destroy(&current->lock);
660                                 ast_free(current);
661                         }
662                         AST_LIST_TRAVERSE_SAFE_END;
663
664                         AST_LIST_REMOVE_CURRENT(&odbc_list, list);
665                         ast_free(class);
666                 }
667         }
668         AST_LIST_TRAVERSE_SAFE_END;
669         AST_LIST_UNLOCK(&odbc_list);
670
671         return 0;
672 }
673
674 static int unload_module(void)
675 {
676         /* Prohibit unloading */
677         return -1;
678 }
679
680 static int load_module(void)
681 {
682         if(load_odbc_config() == -1)
683                 return AST_MODULE_LOAD_DECLINE;
684         ast_cli_register_multiple(cli_odbc, sizeof(cli_odbc) / sizeof(struct ast_cli_entry));
685         ast_log(LOG_NOTICE, "res_odbc loaded.\n");
686         return 0;
687 }
688
689 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC resource",
690                 .load = load_module,
691                 .unload = unload_module,
692                 .reload = reload,
693                );