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