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