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