add back braces that were removed in a recent commit that leave a bunch of
[asterisk/asterisk.git] / res / res_config_pgsql.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Copyright (C) 1999-2005, Digium, Inc.
5  * 
6  * Manuel Guesdon <mguesdon@oxymium.net> - Postgresql RealTime Driver Author/Adaptor
7  * Mark Spencer <markster@digium.com>  - Asterisk Author
8  * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
9  *
10  * res_config_pgsql.c <Postgresql plugin for RealTime configuration engine>
11  *
12  * v1.0   - (07-11-05) - Initial version based on res_config_mysql v2.0
13  */
14
15 /*! \file
16  *
17  * \brief Postgresql plugin for Asterisk RealTime Architecture
18  *
19  * \author Mark Spencer <markster@digium.com>
20  * \author Manuel Guesdon <mguesdon@oxymium.net> - Postgresql RealTime Driver Author/Adaptor
21  *
22  * \arg http://www.postgresql.org
23  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <libpq-fe.h>   /* PostgreSQL */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/file.h"
34 #include "asterisk/logger.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/config.h"
38 #include "asterisk/module.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/options.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/cli.h"
43
44 static char *res_config_pgsql_desc = "Postgresql RealTime Configuration Driver";
45
46 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
47 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
48 PGconn*         pgsqlConn = NULL;
49 #define MAX_DB_OPTION_SIZE 64
50 static char   dbhost[MAX_DB_OPTION_SIZE]="";
51 static char   dbuser[MAX_DB_OPTION_SIZE]="";
52 static char   dbpass[MAX_DB_OPTION_SIZE]="";
53 static char   dbname[MAX_DB_OPTION_SIZE]="";
54 static char   dbsock[MAX_DB_OPTION_SIZE]="";
55 static int    dbport=5432;
56 static time_t connect_time=0;
57
58 static int parse_config(void);
59 static int pgsql_reconnect(const char *database);
60 static int realtime_pgsql_status(int fd, int argc, char **argv);
61
62 LOCAL_USER_DECL;
63
64 static char cli_realtime_pgsql_status_usage[] =
65 "Usage: realtime pgsql status\n"
66 "       Shows connection information for the Postgresql RealTime driver\n";
67
68 static struct ast_cli_entry cli_realtime_pgsql_status = {
69         { "realtime", "pgsql", "status", NULL }, realtime_pgsql_status,
70         "Shows connection information for the Postgresql RealTime driver", cli_realtime_pgsql_status_usage, NULL };
71
72 static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap)
73 {
74         PGresult *result = NULL;
75         int num_rows = 0;
76         char sql[256];
77         char *stringp;
78         char *chunk;
79         char *op;
80         const char *newparam, *newval;
81         struct ast_variable *var=NULL, *prev=NULL;
82
83         if(!table) {
84                 ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
85                 return NULL;
86         }
87
88         /* Get the first parameter and first value in our list of passed paramater/value pairs */
89         newparam = va_arg(ap, const char *);
90         newval = va_arg(ap, const char *);
91         if(!newparam || !newval)  {
92                 ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
93                 if (pgsqlConn)
94                   {
95                     PQfinish(pgsqlConn);
96                     pgsqlConn=NULL;
97                   };
98                 return NULL;
99         }
100
101         /* Create the first part of the query using the first parameter/value pairs we just extracted
102            If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
103         op = strchr(newparam, ' ')?"":" =";
104
105         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
106         while((newparam = va_arg(ap, const char *))) {
107                 newval = va_arg(ap, const char *);
108                 if(!strchr(newparam, ' ')) op = " ="; else op = "";
109                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
110         }
111         va_end(ap);
112
113         /* We now have our complete statement; Lets connect to the server and execute it. */
114         ast_mutex_lock(&pgsql_lock);
115         if(!pgsql_reconnect(database)) {
116                 ast_mutex_unlock(&pgsql_lock);
117                 return NULL;
118         }
119         
120         if(!(result=PQexec(pgsqlConn, sql))) {
121                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
122                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
123                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
124                 ast_mutex_unlock(&pgsql_lock);
125                 return NULL;
126         }
127         else {
128           ExecStatusType result_status=PQresultStatus(result);
129           if (result_status!=PGRES_COMMAND_OK
130               && result_status!=PGRES_TUPLES_OK
131               && result_status!=PGRES_NONFATAL_ERROR)
132             {
133                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
134                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
135                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", 
136                         PQresultErrorMessage(result),PQresStatus(result_status));
137                 ast_mutex_unlock(&pgsql_lock);
138                 return NULL;
139             }
140         }
141            
142         ast_log(LOG_DEBUG, "1Postgresql RealTime: Result=%p Query: %s\n", result, sql);
143
144         if((num_rows=PQntuples(result))>0) {
145           int i = 0;
146           int rowIndex=0;
147           int numFields = PQnfields(result);
148           char** fieldnames=NULL;
149
150           ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows);
151
152           fieldnames=malloc(numFields*sizeof(char*));
153           if (!fieldnames) {
154             /* If I can't alloc memory at this point, why bother doing anything else? */
155             ast_log(LOG_WARNING, "Out of memory!\n");
156             ast_mutex_unlock(&pgsql_lock);
157             PQclear(result);
158             return NULL;
159           }
160           for(i = 0; i < numFields; i++)
161             fieldnames[i]=PQfname(result,i);
162           for(rowIndex = 0; rowIndex < num_rows; rowIndex++) {
163               for(i = 0; i < numFields; i++) {
164                 stringp = PQgetvalue(result,rowIndex,i);
165                 while(stringp) {
166                   chunk = strsep(&stringp, ";");
167                   if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
168                     if(prev) {
169                       prev->next = ast_variable_new(fieldnames[i], chunk);
170                       if (prev->next) {
171                         prev = prev->next;
172                       }
173                     } else {
174                       prev = var = ast_variable_new(fieldnames[i], chunk);
175                     }
176                   }
177                 }
178               }
179           }
180           free(fieldnames);
181         } else {                                
182                 ast_log(LOG_WARNING, "Postgresql RealTime: Could not find any rows in table %s.\n", table);
183         }
184
185         ast_mutex_unlock(&pgsql_lock);
186         PQclear(result);
187
188         return var;
189 }
190
191 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
192 {
193         PGresult *result = NULL;
194         int num_rows = 0;
195         char sql[256];
196         const char *initfield = NULL;
197         char *stringp;
198         char *chunk;
199         char *op;
200         const char *newparam, *newval;
201         struct ast_realloca ra;
202         struct ast_variable *var=NULL;
203         struct ast_config *cfg = NULL;
204         struct ast_category *cat = NULL;
205
206         if(!table) {
207                 ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
208                 return NULL;
209         }
210         
211         memset(&ra, 0, sizeof(ra));
212
213         cfg = ast_config_new();
214         if (!cfg) {
215                 /* If I can't alloc memory at this point, why bother doing anything else? */
216                 ast_log(LOG_WARNING, "Out of memory!\n");
217                 return NULL;
218         }
219
220         /* Get the first parameter and first value in our list of passed paramater/value pairs */
221         newparam = va_arg(ap, const char *);
222         newval = va_arg(ap, const char *);
223         if(!newparam || !newval)  {
224                 ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
225                 if (pgsqlConn)
226                   {
227                     PQfinish(pgsqlConn);
228                     pgsqlConn=NULL;
229                   };
230                 return NULL;
231         }
232
233         initfield = ast_strdupa(newparam);
234         if(initfield && (op = strchr(initfield, ' '))) {
235                 *op = '\0';
236         }
237
238         /* Create the first part of the query using the first parameter/value pairs we just extracted
239            If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
240
241         if(!strchr(newparam, ' ')) op = " ="; else op = "";
242
243         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
244         while((newparam = va_arg(ap, const char *))) {
245                 newval = va_arg(ap, const char *);
246                 if(!strchr(newparam, ' ')) op = " ="; else op = "";
247                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
248         }
249
250         if(initfield) {
251                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
252         }
253
254         va_end(ap);
255
256         /* We now have our complete statement; Lets connect to the server and execute it. */
257         ast_mutex_lock(&pgsql_lock);
258         if(!pgsql_reconnect(database)) {
259                 ast_mutex_unlock(&pgsql_lock);
260                 return NULL;
261         }
262
263         if(!(result=PQexec(pgsqlConn, sql))) {
264                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
265                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
266                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
267                 ast_mutex_unlock(&pgsql_lock);
268                 return NULL;
269         }
270         else {
271           ExecStatusType result_status=PQresultStatus(result);
272           if (result_status!=PGRES_COMMAND_OK
273               && result_status!=PGRES_TUPLES_OK
274               && result_status!=PGRES_NONFATAL_ERROR)
275             {
276                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
277                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
278                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", 
279                         PQresultErrorMessage(result),PQresStatus(result_status));
280                 ast_mutex_unlock(&pgsql_lock);
281                 return NULL;
282             }
283         }
284
285         ast_log(LOG_DEBUG, "2Postgresql RealTime: Result=%p Query: %s\n", result, sql);
286
287         if((num_rows=PQntuples(result))>0) {
288           int numFields = PQnfields(result);
289           int i = 0;
290           int rowIndex=0;
291           char** fieldnames=NULL;
292
293           ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows);
294           
295           fieldnames=malloc(numFields*sizeof(char*));
296           if (!fieldnames) {
297             /* If I can't alloc memory at this point, why bother doing anything else? */
298             ast_log(LOG_WARNING, "Out of memory!\n");
299             ast_mutex_unlock(&pgsql_lock);
300             PQclear(result);
301             return NULL;
302           }
303           for(i = 0; i < numFields; i++)
304             fieldnames[i]=PQfname(result,i);
305
306           for(rowIndex=0;rowIndex<num_rows;rowIndex++)
307             {
308               var = NULL;
309               cat = ast_category_new("");
310               if(!cat) {
311                 ast_log(LOG_WARNING, "Out of memory!\n");
312                 continue;
313               }
314               for(i = 0; i < numFields; i++) {
315                 stringp = PQgetvalue(result,rowIndex,i);
316                 while(stringp) {
317                   chunk = strsep(&stringp, ";");
318                   if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
319                     if(initfield && !strcmp(initfield, fieldnames[i])) {
320                       ast_category_rename(cat, chunk);
321                     }
322                     var = ast_variable_new(fieldnames[i], chunk);
323                     ast_variable_append(cat, var);
324                   }
325                 }
326               }
327               ast_category_append(cfg, cat);
328             }
329           free(fieldnames);
330         } else {
331                 ast_log(LOG_WARNING, "Postgresql RealTime: Could not find any rows in table %s.\n", table);
332         }
333
334         ast_mutex_unlock(&pgsql_lock);
335         PQclear(result);
336
337         return cfg;
338 }
339
340 static int update_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
341 {
342         PGresult* result = NULL;
343         int numrows = 0;
344         char sql[256];
345         const char *newparam, *newval;
346
347         if(!table) {
348                 ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
349                return -1;
350         }
351
352         /* Get the first parameter and first value in our list of passed paramater/value pairs */
353         newparam = va_arg(ap, const char *);
354         newval = va_arg(ap, const char *);
355         if(!newparam || !newval)  {
356                 ast_log(LOG_WARNING, "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
357                 if (pgsqlConn)
358                   {
359                     PQfinish(pgsqlConn);
360                     pgsqlConn=NULL;
361                   };
362                return -1;
363         }
364
365         /* Create the first part of the query using the first parameter/value pairs we just extracted
366            If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
367
368         snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval);
369         while((newparam = va_arg(ap, const char *))) {
370                 newval = va_arg(ap, const char *);
371                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval);
372         }
373         va_end(ap);
374         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
375
376         ast_log(LOG_DEBUG,"Postgresql RealTime: Update SQL: %s\n", sql);
377
378         /* We now have our complete statement; Lets connect to the server and execute it. */
379         ast_mutex_lock(&pgsql_lock);
380         if(!pgsql_reconnect(database)) {
381                 ast_mutex_unlock(&pgsql_lock);
382                 return -1;
383         }
384
385         if(!(result=PQexec(pgsqlConn, sql))) {
386                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
387                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
388                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
389                 ast_mutex_unlock(&pgsql_lock);
390                 return -1;
391         }
392         else {
393           ExecStatusType result_status=PQresultStatus(result);
394           if (result_status!=PGRES_COMMAND_OK
395               && result_status!=PGRES_TUPLES_OK
396               && result_status!=PGRES_NONFATAL_ERROR)
397             {
398                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
399                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
400                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", 
401                         PQresultErrorMessage(result),PQresStatus(result_status));
402                 ast_mutex_unlock(&pgsql_lock);
403                 return -1;
404             }
405         }
406
407         numrows = atoi(PQcmdTuples(result));
408         ast_mutex_unlock(&pgsql_lock);
409
410         ast_log(LOG_DEBUG,"Postgresql RealTime: Updated %d rows on table: %s\n", numrows, table);
411
412         /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
413          * An integer greater than zero indicates the number of rows affected
414          * Zero indicates that no records were updated
415          * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
416         */
417
418         if(numrows >= 0)
419                 return (int)numrows;
420
421         return -1;
422 }
423
424 static struct ast_config *config_pgsql(const char *database, const char *table, const char *file, struct ast_config *cfg)
425 {
426         PGresult *result = NULL;
427         long num_rows;
428         struct ast_variable *new_v;
429         struct ast_category *cur_cat = NULL;
430         char sql[250] = "";
431         char last[80] = "";
432         int last_cat_metric = 0;
433
434         last[0] = '\0';
435
436         if(!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
437                 ast_log(LOG_WARNING, "Postgresql RealTime: Cannot configure myself.\n");
438                 return NULL;
439         }
440
441         snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
442
443         ast_log(LOG_DEBUG, "Postgresql RealTime: Static SQL: %s\n", sql);
444
445         /* We now have our complete statement; Lets connect to the server and execute it. */
446         ast_mutex_lock(&pgsql_lock);
447         if(!pgsql_reconnect(database)) {
448                 ast_mutex_unlock(&pgsql_lock);
449                 return NULL;
450         }
451
452         if(!(result=PQexec(pgsqlConn, sql))) {
453                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
454                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
455                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
456                 ast_mutex_unlock(&pgsql_lock);
457                 return NULL;
458         }
459         else {
460           ExecStatusType result_status=PQresultStatus(result);
461           if (result_status!=PGRES_COMMAND_OK
462               && result_status!=PGRES_TUPLES_OK
463               && result_status!=PGRES_NONFATAL_ERROR)
464             {
465                 ast_log(LOG_WARNING, "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
466                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
467                 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n", 
468                         PQresultErrorMessage(result),PQresStatus(result_status));
469                 ast_mutex_unlock(&pgsql_lock);
470                 return NULL;
471             }
472         }
473
474         if((num_rows=PQntuples(result))>0) {
475           int numFields = PQnfields(result);
476           int i = 0;
477           int rowIndex=0;
478           char** fieldnames=NULL;
479
480           ast_log(LOG_DEBUG, "Postgresql RealTime: Found %ld rows.\n", num_rows);
481           
482           fieldnames=malloc(numFields*sizeof(char*));
483           if (!fieldnames) {
484             /* If I can't alloc memory at this point, why bother doing anything else? */
485             ast_log(LOG_WARNING, "Out of memory!\n");
486             ast_mutex_unlock(&pgsql_lock);
487             PQclear(result);
488             return NULL;
489           }
490           for(i = 0; i < numFields; i++)
491             fieldnames[i]=PQfname(result,i);
492
493           for(rowIndex=0;rowIndex<num_rows;rowIndex++)
494             {
495               char* field_category=PQgetvalue(result,rowIndex,0);
496               char* field_var_name=PQgetvalue(result,rowIndex,1);
497               char* field_var_val=PQgetvalue(result,rowIndex,2);
498               char* field_cat_metric=PQgetvalue(result,rowIndex,3);
499                         if(!strcmp(field_var_name, "#include")) {
500                                 if (!ast_config_internal_load(field_var_val, cfg)) {
501                                         PQclear(result);
502                                         ast_mutex_unlock(&pgsql_lock);
503                                         return NULL;
504                                 }
505                                 continue;
506                         }
507
508                         if(strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
509                                 cur_cat = ast_category_new(field_category);
510                                 if (!cur_cat) {
511                                         ast_log(LOG_WARNING, "Out of memory!\n");
512                                         break;
513                                 }
514                                 strcpy(last, field_category);
515                                 last_cat_metric = atoi(field_cat_metric);
516                                 ast_category_append(cfg, cur_cat);
517                         }
518                         new_v = ast_variable_new(field_var_name, field_var_val);
519                         ast_variable_append(cur_cat, new_v);
520                 }
521         } else {
522                 ast_log(LOG_WARNING, "Postgresql RealTime: Could not find config '%s' in database.\n", file);
523         }
524
525         PQclear(result);
526         ast_mutex_unlock(&pgsql_lock);
527
528         return cfg;
529 }
530
531 static struct ast_config_engine pgsql_engine = {
532         .name = "pgsql",
533         .load_func = config_pgsql,
534         .realtime_func = realtime_pgsql,
535         .realtime_multi_func = realtime_multi_pgsql,
536         .update_func = update_pgsql
537 };
538
539 int load_module (void)
540 {
541         parse_config();
542
543         ast_mutex_lock(&pgsql_lock);
544
545         if(!pgsql_reconnect(NULL)) {
546                 ast_log(LOG_WARNING, "Postgresql RealTime: Couldn't establish connection. Check debug.\n");
547                 ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
548         }
549
550         ast_config_engine_register(&pgsql_engine);
551         if(option_verbose) {
552                 ast_verbose("Postgresql RealTime driver loaded.\n");
553         }
554         ast_cli_register(&cli_realtime_pgsql_status);
555
556         ast_mutex_unlock(&pgsql_lock);
557
558         return 0;
559 }
560
561 int unload_module (void)
562 {
563         /* Aquire control before doing anything to the module itself. */
564         ast_mutex_lock(&pgsql_lock);
565
566         if (pgsqlConn)
567           {
568             PQfinish(pgsqlConn);
569             pgsqlConn=NULL;
570           };
571         ast_cli_unregister(&cli_realtime_pgsql_status);
572         ast_config_engine_deregister(&pgsql_engine);
573         if(option_verbose) {
574                 ast_verbose("Postgresql RealTime unloaded.\n");
575         }
576
577         STANDARD_HANGUP_LOCALUSERS;
578
579         /* Unlock so something else can destroy the lock. */
580         ast_mutex_unlock(&pgsql_lock);
581
582         return 0;
583 }
584
585 int reload (void)
586 {
587         /* Aquire control before doing anything to the module itself. */
588         ast_mutex_lock(&pgsql_lock);
589
590         if (pgsqlConn)
591           {
592             PQfinish(pgsqlConn);
593             pgsqlConn=NULL;
594           };
595         parse_config();
596
597         if(!pgsql_reconnect(NULL)) {
598                 ast_log(LOG_WARNING, "Postgresql RealTime: Couldn't establish connection. Check debug.\n");
599                 ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
600         }
601
602         ast_verbose(VERBOSE_PREFIX_2 "Postgresql RealTime reloaded.\n");
603
604         /* Done reloading. Release lock so others can now use driver. */
605         ast_mutex_unlock(&pgsql_lock);
606
607         return 0;
608 }
609
610 int parse_config (void)
611 {
612         struct ast_config *config;
613         char *s;
614
615         config = ast_config_load(RES_CONFIG_PGSQL_CONF);
616
617         if(config) {
618                 if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
619                         ast_log(LOG_WARNING, "Postgresql RealTime: No database user found, using 'asterisk' as default.\n");
620                         strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
621                 } else {
622                         strncpy(dbuser, s, sizeof(dbuser) - 1);
623                 }
624
625                 if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
626                         ast_log(LOG_WARNING, "Postgresql RealTime: No database password found, using 'asterisk' as default.\n");
627                         strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
628                 } else {
629                         strncpy(dbpass, s, sizeof(dbpass) - 1);
630                 }
631
632                 if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
633                         ast_log(LOG_WARNING, "Postgresql RealTime: No database host found, using localhost via socket.\n");
634                         dbhost[0] = '\0';
635                 } else {
636                         strncpy(dbhost, s, sizeof(dbhost) - 1);
637                 }
638
639                 if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
640                         ast_log(LOG_WARNING, "Postgresql RealTime: No database name found, using 'asterisk' as default.\n");
641                         strncpy(dbname, "asterisk", sizeof(dbname) - 1);
642                 } else {
643                         strncpy(dbname, s, sizeof(dbname) - 1);
644                 }
645
646                 if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
647                         ast_log(LOG_WARNING, "Postgresql RealTime: No database port found, using 5432 as default.\n");
648                         dbport = 5432;
649                 } else {
650                         dbport = atoi(s);
651                 }
652
653                 if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
654                         ast_log(LOG_WARNING, "Postgresql RealTime: No database socket found, using '/tmp/pgsql.sock' as default.\n");
655                         strncpy(dbsock, "/tmp/pgsql.sock", sizeof(dbsock) - 1);
656                 } else {
657                         strncpy(dbsock, s, sizeof(dbsock) - 1);
658                 }
659         }
660         ast_config_destroy(config);
661
662         if(dbhost) {
663                 ast_log(LOG_DEBUG, "Postgresql RealTime Host: %s\n", dbhost);
664                 ast_log(LOG_DEBUG, "Postgresql RealTime Port: %i\n", dbport);
665         } else {
666                 ast_log(LOG_DEBUG, "Postgresql RealTime Socket: %s\n", dbsock);
667         }
668         ast_log(LOG_DEBUG, "Postgresql RealTime User: %s\n", dbuser);
669         ast_log(LOG_DEBUG, "Postgresql RealTime Password: %s\n", dbpass);
670         ast_log(LOG_DEBUG, "Postgresql RealTime DBName: %s\n", dbname);
671
672         return 1;
673 }
674
675 const char *description(void)
676 {
677         return res_config_pgsql_desc;
678 }
679
680 int usecount (void)
681 {
682         /* Try and get a lock. If unsuccessful, than that means another thread is using the pgsql object. */
683         if(ast_mutex_trylock(&pgsql_lock)) {
684                 ast_log(LOG_DEBUG, "Postgresql RealTime: Module usage count is 1.\n");
685                 return 1;
686         }
687         ast_mutex_unlock(&pgsql_lock);
688         return 0;
689 }
690
691 const char *key()
692 {
693         return ASTERISK_GPL_KEY;
694 }
695
696 static int pgsql_reconnect(const char *database)
697 {
698         char my_database[50];
699
700         ast_copy_string(my_database, S_OR(database,dbname), sizeof(my_database));
701
702         /* mutex lock should have been locked before calling this function. */
703
704         if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
705               PQfinish(pgsqlConn);
706               pgsqlConn=NULL;
707             }
708
709         if((!pgsqlConn) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
710           char* connInfo=NULL;
711           unsigned int size=100
712             +strlen(dbhost)
713             +strlen(dbuser)
714             +strlen(dbpass)
715             +strlen(my_database);
716           connInfo=malloc(size);
717           if (!connInfo)
718             {
719               ast_log(LOG_WARNING, "Postgresql RealTime: Insufficient memory to allocate Pgsql resource.\n");
720               return 0;
721             }
722           else
723             {
724               sprintf(connInfo,"host=%s port=%d dbname=%s user=%s password=%s",
725                       dbhost,dbport,my_database,dbuser,dbpass);
726               ast_log(LOG_DEBUG, "%u connInfo=%s\n", size,connInfo);
727               pgsqlConn=PQconnectdb(connInfo);    
728               ast_log(LOG_DEBUG, "%u connInfo=%s\n", size,connInfo);
729               free(connInfo);
730               connInfo=NULL;
731               ast_log(LOG_DEBUG, "pgsqlConn=%p\n", pgsqlConn);
732               if (pgsqlConn)
733                 {
734                   ast_log(LOG_DEBUG, "Postgresql RealTime: Successfully connected to database.\n");
735                   connect_time = time(NULL);
736                   return 1;
737                 } else {
738                 ast_log(LOG_ERROR, "Postgresql RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost);
739                 ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n", PQresultErrorMessage(NULL));
740                 return 0;
741               }
742             }
743         } else {
744
745             ast_log(LOG_DEBUG, "Postgresql RealTime: Everything is fine.\n");
746             return 1;
747         }
748 }
749
750 static int realtime_pgsql_status(int fd, int argc, char **argv)
751 {
752         char status[256], status2[100] = "";
753         int ctime = time(NULL) - connect_time;
754
755         if(pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
756                 if(dbhost) {
757                         snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
758                 } else if(dbsock) {
759                         snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
760                 } else {
761                         snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
762                 }
763
764                 if(dbuser && *dbuser) {
765                         snprintf(status2, 99, " with username %s", dbuser);
766                 }
767
768                 if (ctime > 31536000) {
769                         ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
770                 } else if (ctime > 86400) {
771                         ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
772                 } else if (ctime > 3600) {
773                         ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
774                 } else if (ctime > 60) {
775                         ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
776                 } else {
777                         ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
778                 }
779
780                 return RESULT_SUCCESS;
781         } else {
782                 return RESULT_FAILURE;
783         }
784 }