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