Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


res_config_pgsql.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Copyright (C) 1999-2010, 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  * \extref PostgreSQL http://www.postgresql.org
23  */
24 
25 /*** MODULEINFO
26  <depend>pgsql</depend>
27  <support_level>extended</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413224 $")
33 
34 #include <libpq-fe.h> /* PostgreSQL */
35 
36 #include "asterisk/file.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/config.h"
40 #include "asterisk/module.h"
41 #include "asterisk/lock.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/cli.h"
44 
51 
52 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
53 
54 static PGconn *pgsqlConn = NULL;
55 static int version;
56 #define has_schema_support (version > 70300 ? 1 : 0)
57 
58 #define MAX_DB_OPTION_SIZE 64
59 
60 struct columns {
61  char *name;
62  char *type;
63  int len;
64  unsigned int notnull:1;
65  unsigned int hasdefault:1;
67 };
68 
69 struct tables {
73  char name[0];
74 };
75 
77 
78 static char dbhost[MAX_DB_OPTION_SIZE] = "";
79 static char dbuser[MAX_DB_OPTION_SIZE] = "";
80 static char dbpass[MAX_DB_OPTION_SIZE] = "";
81 static char dbname[MAX_DB_OPTION_SIZE] = "";
82 static char dbsock[MAX_DB_OPTION_SIZE] = "";
83 static int dbport = 5432;
84 static time_t connect_time = 0;
85 
86 static int parse_config(int reload);
87 static int pgsql_reconnect(const char *database);
88 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
89 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
90 
92 
93 static struct ast_cli_entry cli_realtime[] = {
94  AST_CLI_DEFINE(handle_cli_realtime_pgsql_status, "Shows connection information for the PostgreSQL RealTime driver"),
95  AST_CLI_DEFINE(handle_cli_realtime_pgsql_cache, "Shows cached tables within the PostgreSQL realtime driver"),
96 };
97 
98 #define ESCAPE_STRING(buffer, stringname) \
99  do { \
100  int len = strlen(stringname); \
101  struct ast_str *semi = ast_str_thread_get(&semibuf_buf, len * 3 + 1); \
102  const char *chunk = stringname; \
103  ast_str_reset(semi); \
104  for (; *chunk; chunk++) { \
105  if (strchr(";^", *chunk)) { \
106  ast_str_append(&semi, 0, "^%02hhX", *chunk); \
107  } else { \
108  ast_str_append(&semi, 0, "%c", *chunk); \
109  } \
110  } \
111  if (ast_str_strlen(semi) > (ast_str_size(buffer) - 1) / 2) { \
112  ast_str_make_space(&buffer, ast_str_strlen(semi) * 2 + 1); \
113  } \
114  PQescapeStringConn(pgsqlConn, ast_str_buffer(buffer), ast_str_buffer(semi), ast_str_size(buffer), &pgresult); \
115  } while (0)
116 
117 static void destroy_table(struct tables *table)
118 {
119  struct columns *column;
120  ast_rwlock_wrlock(&table->lock);
121  while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
122  ast_free(column);
123  }
124  ast_rwlock_unlock(&table->lock);
125  ast_rwlock_destroy(&table->lock);
126  ast_free(table);
127 }
128 
129 static struct tables *find_table(const char *database, const char *orig_tablename)
130 {
131  struct columns *column;
132  struct tables *table;
133  struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
134  char *pgerror;
135  RAII_VAR(PGresult *, result, NULL, PQclear);
136  char *fname, *ftype, *flen, *fnotnull, *fdef;
137  int i, rows;
138 
141  if (!strcasecmp(table->name, orig_tablename)) {
142  ast_debug(1, "Found table in cache; now locking\n");
143  ast_rwlock_rdlock(&table->lock);
144  ast_debug(1, "Lock cached table; now returning\n");
146  return table;
147  }
148  }
149 
150  ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
151 
152  /* Not found, scan the table */
153  if (has_schema_support) {
154  char *schemaname, *tablename;
155  if (strchr(orig_tablename, '.')) {
156  schemaname = ast_strdupa(orig_tablename);
157  tablename = strchr(schemaname, '.');
158  *tablename++ = '\0';
159  } else {
160  schemaname = "";
161  tablename = ast_strdupa(orig_tablename);
162  }
163 
164  /* Escape special characters in schemaname */
165  if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
166  char *tmp = schemaname, *ptr;
167 
168  ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
169  for (; *tmp; tmp++) {
170  if (strchr("\\'", *tmp)) {
171  *ptr++ = *tmp;
172  }
173  *ptr++ = *tmp;
174  }
175  *ptr = '\0';
176  }
177  /* Escape special characters in tablename */
178  if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
179  char *tmp = tablename, *ptr;
180 
181  ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
182  for (; *tmp; tmp++) {
183  if (strchr("\\'", *tmp)) {
184  *ptr++ = *tmp;
185  }
186  *ptr++ = *tmp;
187  }
188  *ptr = '\0';
189  }
190 
191  ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
192  tablename,
193  ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
194  } else {
195  /* Escape special characters in tablename */
196  if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
197  const char *tmp = orig_tablename;
198  char *ptr;
199 
200  orig_tablename = ptr = ast_alloca(strlen(tmp) * 2 + 1);
201  for (; *tmp; tmp++) {
202  if (strchr("\\'", *tmp)) {
203  *ptr++ = *tmp;
204  }
205  *ptr++ = *tmp;
206  }
207  *ptr = '\0';
208  }
209 
210  ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", orig_tablename);
211  }
212 
214  if (!pgsql_reconnect(database)) {
217  return NULL;
218  }
219 
220  result = PQexec(pgsqlConn, ast_str_buffer(sql));
221  ast_debug(1, "Query of table structure complete. Now retrieving results.\n");
222  if (PQresultStatus(result) != PGRES_TUPLES_OK) {
223  pgerror = PQresultErrorMessage(result);
224  ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
227  return NULL;
228  }
229 
230  if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
231  ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
234  return NULL;
235  }
236  strcpy(table->name, orig_tablename); /* SAFE */
237  ast_rwlock_init(&table->lock);
239 
240  rows = PQntuples(result);
241  for (i = 0; i < rows; i++) {
242  fname = PQgetvalue(result, i, 0);
243  ftype = PQgetvalue(result, i, 1);
244  flen = PQgetvalue(result, i, 2);
245  fnotnull = PQgetvalue(result, i, 3);
246  fdef = PQgetvalue(result, i, 4);
247  ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
248 
249  if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
250  ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
251  destroy_table(table);
254  return NULL;
255  }
256 
257  if (strcmp(flen, "-1") == 0) {
258  /* Some types, like chars, have the length stored in a different field */
259  flen = PQgetvalue(result, i, 5);
260  sscanf(flen, "%30d", &column->len);
261  column->len -= 4;
262  } else {
263  sscanf(flen, "%30d", &column->len);
264  }
265  column->name = (char *)column + sizeof(*column);
266  column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
267  strcpy(column->name, fname);
268  strcpy(column->type, ftype);
269  if (*fnotnull == 't') {
270  column->notnull = 1;
271  } else {
272  column->notnull = 0;
273  }
274  if (!ast_strlen_zero(fdef)) {
275  column->hasdefault = 1;
276  } else {
277  column->hasdefault = 0;
278  }
279  AST_LIST_INSERT_TAIL(&table->columns, column, list);
280  }
281 
283  ast_rwlock_rdlock(&table->lock);
286  return table;
287 }
288 
289 #define release_table(table) ast_rwlock_unlock(&(table)->lock);
290 
291 static struct columns *find_column(struct tables *t, const char *colname)
292 {
293  struct columns *column;
294 
295  /* Check that the column exists in the table */
296  AST_LIST_TRAVERSE(&t->columns, column, list) {
297  if (strcmp(column->name, colname) == 0) {
298  return column;
299  }
300  }
301  return NULL;
302 }
303 
304 static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap)
305 {
306  RAII_VAR(PGresult *, result, NULL, PQclear);
307  int num_rows = 0, pgresult;
308  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
309  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
310  char *stringp;
311  char *chunk;
312  char *op;
313  const char *newparam, *newval;
314  struct ast_variable *var = NULL, *prev = NULL;
315 
316  /*
317  * Ignore database from the extconfig.conf since it was
318  * configured by res_pgsql.conf.
319  */
320  database = dbname;
321 
322  if (!tablename) {
323  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
324  return NULL;
325  }
326 
327  /* Get the first parameter and first value in our list of passed paramater/value pairs */
328  newparam = va_arg(ap, const char *);
329  newval = va_arg(ap, const char *);
330  if (!newparam || !newval) {
332  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
333  if (pgsqlConn) {
334  PQfinish(pgsqlConn);
335  pgsqlConn = NULL;
336  }
337  return NULL;
338  }
339 
340  /* Create the first part of the query using the first parameter/value pairs we just extracted
341  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
342  op = strchr(newparam, ' ') ? "" : " =";
343 
344  ESCAPE_STRING(escapebuf, newval);
345  if (pgresult) {
346  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
347  return NULL;
348  }
349 
350  ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
351  while ((newparam = va_arg(ap, const char *))) {
352  newval = va_arg(ap, const char *);
353  if (!strchr(newparam, ' '))
354  op = " =";
355  else
356  op = "";
357 
358  ESCAPE_STRING(escapebuf, newval);
359  if (pgresult) {
360  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
361  return NULL;
362  }
363 
364  ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
365  }
366 
367  /* We now have our complete statement; Lets connect to the server and execute it. */
369  if (!pgsql_reconnect(database)) {
371  return NULL;
372  }
373 
374  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
376  "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
377  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
378  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
380  return NULL;
381  } else {
382  ExecStatusType result_status = PQresultStatus(result);
383  if (result_status != PGRES_COMMAND_OK
384  && result_status != PGRES_TUPLES_OK
385  && result_status != PGRES_NONFATAL_ERROR) {
387  "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database);
388  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
389  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
390  PQresultErrorMessage(result), PQresStatus(result_status));
392  return NULL;
393  }
394  }
395 
396  ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
397 
398  if ((num_rows = PQntuples(result)) > 0) {
399  int i = 0;
400  int rowIndex = 0;
401  int numFields = PQnfields(result);
402  char **fieldnames = NULL;
403 
404  ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
405 
406  if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
408  return NULL;
409  }
410  for (i = 0; i < numFields; i++)
411  fieldnames[i] = PQfname(result, i);
412  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
413  for (i = 0; i < numFields; i++) {
414  stringp = PQgetvalue(result, rowIndex, i);
415  while (stringp) {
416  chunk = strsep(&stringp, ";");
417  if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
418  if (prev) {
419  prev->next = ast_variable_new(fieldnames[i], chunk, "");
420  if (prev->next) {
421  prev = prev->next;
422  }
423  } else {
424  prev = var = ast_variable_new(fieldnames[i], chunk, "");
425  }
426  }
427  }
428  }
429  }
430  ast_free(fieldnames);
431  } else {
432  ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
433  }
434 
436 
437  return var;
438 }
439 
440 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
441 {
442  RAII_VAR(PGresult *, result, NULL, PQclear);
443  int num_rows = 0, pgresult;
444  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
445  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
446  const char *initfield = NULL;
447  char *stringp;
448  char *chunk;
449  char *op;
450  const char *newparam, *newval;
451  struct ast_variable *var = NULL;
452  struct ast_config *cfg = NULL;
453  struct ast_category *cat = NULL;
454 
455  /*
456  * Ignore database from the extconfig.conf since it was
457  * configured by res_pgsql.conf.
458  */
459  database = dbname;
460 
461  if (!table) {
462  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
463  return NULL;
464  }
465 
466  if (!(cfg = ast_config_new()))
467  return NULL;
468 
469  /* Get the first parameter and first value in our list of passed paramater/value pairs */
470  newparam = va_arg(ap, const char *);
471  newval = va_arg(ap, const char *);
472  if (!newparam || !newval) {
474  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
475  if (pgsqlConn) {
476  PQfinish(pgsqlConn);
477  pgsqlConn = NULL;
478  }
479  ast_config_destroy(cfg);
480  return NULL;
481  }
482 
483  initfield = ast_strdupa(newparam);
484  if ((op = strchr(initfield, ' '))) {
485  *op = '\0';
486  }
487 
488  /* Create the first part of the query using the first parameter/value pairs we just extracted
489  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
490 
491  if (!strchr(newparam, ' '))
492  op = " =";
493  else
494  op = "";
495 
496  ESCAPE_STRING(escapebuf, newval);
497  if (pgresult) {
498  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
499  ast_config_destroy(cfg);
500  return NULL;
501  }
502 
503  ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
504  while ((newparam = va_arg(ap, const char *))) {
505  newval = va_arg(ap, const char *);
506  if (!strchr(newparam, ' '))
507  op = " =";
508  else
509  op = "";
510 
511  ESCAPE_STRING(escapebuf, newval);
512  if (pgresult) {
513  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
514  ast_config_destroy(cfg);
515  return NULL;
516  }
517 
518  ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
519  }
520 
521  if (initfield) {
522  ast_str_append(&sql, 0, " ORDER BY %s", initfield);
523  }
524 
525 
526  /* We now have our complete statement; Lets connect to the server and execute it. */
528  if (!pgsql_reconnect(database)) {
530  ast_config_destroy(cfg);
531  return NULL;
532  }
533 
534  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
536  "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
537  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
538  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
540  ast_config_destroy(cfg);
541  return NULL;
542  } else {
543  ExecStatusType result_status = PQresultStatus(result);
544  if (result_status != PGRES_COMMAND_OK
545  && result_status != PGRES_TUPLES_OK
546  && result_status != PGRES_NONFATAL_ERROR) {
548  "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
549  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
550  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
551  PQresultErrorMessage(result), PQresStatus(result_status));
553  ast_config_destroy(cfg);
554  return NULL;
555  }
556  }
557 
558  ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
559 
560  if ((num_rows = PQntuples(result)) > 0) {
561  int numFields = PQnfields(result);
562  int i = 0;
563  int rowIndex = 0;
564  char **fieldnames = NULL;
565 
566  ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
567 
568  if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
570  ast_config_destroy(cfg);
571  return NULL;
572  }
573  for (i = 0; i < numFields; i++)
574  fieldnames[i] = PQfname(result, i);
575 
576  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
577  var = NULL;
578  if (!(cat = ast_category_new("","",99999)))
579  continue;
580  for (i = 0; i < numFields; i++) {
581  stringp = PQgetvalue(result, rowIndex, i);
582  while (stringp) {
583  chunk = strsep(&stringp, ";");
584  if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
585  if (initfield && !strcmp(initfield, fieldnames[i])) {
586  ast_category_rename(cat, chunk);
587  }
588  var = ast_variable_new(fieldnames[i], chunk, "");
589  ast_variable_append(cat, var);
590  }
591  }
592  }
593  ast_category_append(cfg, cat);
594  }
595  ast_free(fieldnames);
596  } else {
597  ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
598  }
599 
601 
602  return cfg;
603 }
604 
605 static int update_pgsql(const char *database, const char *tablename, const char *keyfield,
606  const char *lookup, va_list ap)
607 {
608  RAII_VAR(PGresult *, result, NULL, PQclear);
609  int numrows = 0, pgresult;
610  const char *newparam, *newval;
611  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
612  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
613  struct tables *table;
614  struct columns *column = NULL;
615 
616  /*
617  * Ignore database from the extconfig.conf since it was
618  * configured by res_pgsql.conf.
619  */
620  database = dbname;
621 
622  if (!tablename) {
623  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
624  return -1;
625  }
626 
627  if (!(table = find_table(database, tablename))) {
628  ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
629  return -1;
630  }
631 
632  /* Get the first parameter and first value in our list of passed paramater/value pairs */
633  newparam = va_arg(ap, const char *);
634  newval = va_arg(ap, const char *);
635  if (!newparam || !newval) {
637  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
638  if (pgsqlConn) {
639  PQfinish(pgsqlConn);
640  pgsqlConn = NULL;
641  }
642  release_table(table);
643  return -1;
644  }
645 
646  /* Check that the column exists in the table */
647  AST_LIST_TRAVERSE(&table->columns, column, list) {
648  if (strcmp(column->name, newparam) == 0) {
649  break;
650  }
651  }
652 
653  if (!column) {
654  ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
655  release_table(table);
656  return -1;
657  }
658 
659  /* Create the first part of the query using the first parameter/value pairs we just extracted
660  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
661 
662  ESCAPE_STRING(escapebuf, newval);
663  if (pgresult) {
664  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
665  release_table(table);
666  return -1;
667  }
668  ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
669 
670  while ((newparam = va_arg(ap, const char *))) {
671  newval = va_arg(ap, const char *);
672 
673  if (!find_column(table, newparam)) {
674  ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
675  continue;
676  }
677 
678  ESCAPE_STRING(escapebuf, newval);
679  if (pgresult) {
680  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
681  release_table(table);
682  return -1;
683  }
684 
685  ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
686  }
687  release_table(table);
688 
689  ESCAPE_STRING(escapebuf, lookup);
690  if (pgresult) {
691  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
692  return -1;
693  }
694 
695  ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
696 
697  ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
698 
699  /* We now have our complete statement; Lets connect to the server and execute it. */
701  if (!pgsql_reconnect(database)) {
703  return -1;
704  }
705 
706  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
708  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
709  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
710  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
712  return -1;
713  } else {
714  ExecStatusType result_status = PQresultStatus(result);
715  if (result_status != PGRES_COMMAND_OK
716  && result_status != PGRES_TUPLES_OK
717  && result_status != PGRES_NONFATAL_ERROR) {
719  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
720  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
721  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
722  PQresultErrorMessage(result), PQresStatus(result_status));
724  return -1;
725  }
726  }
727 
728  numrows = atoi(PQcmdTuples(result));
730 
731  ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
732 
733  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
734  * An integer greater than zero indicates the number of rows affected
735  * Zero indicates that no records were updated
736  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
737  */
738 
739  if (numrows >= 0)
740  return (int) numrows;
741 
742  return -1;
743 }
744 
745 static int update2_pgsql(const char *database, const char *tablename, va_list ap)
746 {
747  RAII_VAR(PGresult *, result, NULL, PQclear);
748  int numrows = 0, pgresult, first = 1;
749  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
750  const char *newparam, *newval;
751  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
752  struct ast_str *where = ast_str_thread_get(&where_buf, 100);
753  struct tables *table;
754 
755  /*
756  * Ignore database from the extconfig.conf since it was
757  * configured by res_pgsql.conf.
758  */
759  database = dbname;
760 
761  if (!tablename) {
762  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
763  return -1;
764  }
765 
766  if (!escapebuf || !sql || !where) {
767  /* Memory error, already handled */
768  return -1;
769  }
770 
771  if (!(table = find_table(database, tablename))) {
772  ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
773  return -1;
774  }
775 
776  ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
777  ast_str_set(&where, 0, " WHERE");
778 
779  while ((newparam = va_arg(ap, const char *))) {
780  if (!find_column(table, newparam)) {
781  ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
782  release_table(table);
783  return -1;
784  }
785 
786  newval = va_arg(ap, const char *);
787  ESCAPE_STRING(escapebuf, newval);
788  if (pgresult) {
789  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
790  release_table(table);
791  return -1;
792  }
793  ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
794  first = 0;
795  }
796 
797  if (first) {
799  "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
800  if (pgsqlConn) {
801  PQfinish(pgsqlConn);
802  pgsqlConn = NULL;
803  }
804  release_table(table);
805  return -1;
806  }
807 
808  /* Now retrieve the columns to update */
809  first = 1;
810  while ((newparam = va_arg(ap, const char *))) {
811  newval = va_arg(ap, const char *);
812 
813  /* If the column is not within the table, then skip it */
814  if (!find_column(table, newparam)) {
815  ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
816  continue;
817  }
818 
819  ESCAPE_STRING(escapebuf, newval);
820  if (pgresult) {
821  ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
822  release_table(table);
823  return -1;
824  }
825 
826  ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
827  first = 0;
828  }
829  release_table(table);
830 
831  ast_str_append(&sql, 0, "%s", ast_str_buffer(where));
832 
833  ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
834 
835  /* We now have our complete statement; connect to the server and execute it. */
837  if (!pgsql_reconnect(database)) {
839  return -1;
840  }
841 
842  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
844  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
845  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
846  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
848  return -1;
849  } else {
850  ExecStatusType result_status = PQresultStatus(result);
851  if (result_status != PGRES_COMMAND_OK
852  && result_status != PGRES_TUPLES_OK
853  && result_status != PGRES_NONFATAL_ERROR) {
855  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
856  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
857  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
858  PQresultErrorMessage(result), PQresStatus(result_status));
860  return -1;
861  }
862  }
863 
864  numrows = atoi(PQcmdTuples(result));
866 
867  ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
868 
869  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
870  * An integer greater than zero indicates the number of rows affected
871  * Zero indicates that no records were updated
872  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
873  */
874 
875  if (numrows >= 0) {
876  return (int) numrows;
877  }
878 
879  return -1;
880 }
881 
882 static int store_pgsql(const char *database, const char *table, va_list ap)
883 {
884  RAII_VAR(PGresult *, result, NULL, PQclear);
885  int numrows;
886  struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
887  struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
888  struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
889  int pgresult;
890  const char *newparam, *newval;
891 
892  /*
893  * Ignore database from the extconfig.conf since it was
894  * configured by res_pgsql.conf.
895  */
896  database = dbname;
897 
898  if (!table) {
899  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
900  return -1;
901  }
902 
903  /* Get the first parameter and first value in our list of passed paramater/value pairs */
904  newparam = va_arg(ap, const char *);
905  newval = va_arg(ap, const char *);
906  if (!newparam || !newval) {
908  "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
909  if (pgsqlConn) {
910  PQfinish(pgsqlConn);
911  pgsqlConn = NULL;
912  }
913  return -1;
914  }
915 
916  /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
918  if (!pgsql_reconnect(database)) {
920  return -1;
921  }
922 
923  /* Create the first part of the query using the first parameter/value pairs we just extracted
924  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
925  ESCAPE_STRING(buf, newparam);
926  ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
927  ESCAPE_STRING(buf, newval);
928  ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
929  while ((newparam = va_arg(ap, const char *))) {
930  newval = va_arg(ap, const char *);
931  ESCAPE_STRING(buf, newparam);
932  ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
933  ESCAPE_STRING(buf, newval);
934  ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
935  }
936  ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
937 
938  ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
939 
940  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql1)))) {
942  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
943  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
944  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
946  return -1;
947  } else {
948  ExecStatusType result_status = PQresultStatus(result);
949  if (result_status != PGRES_COMMAND_OK
950  && result_status != PGRES_TUPLES_OK
951  && result_status != PGRES_NONFATAL_ERROR) {
953  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
954  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql1));
955  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
956  PQresultErrorMessage(result), PQresStatus(result_status));
958  return -1;
959  }
960  }
961 
962  numrows = atoi(PQcmdTuples(result));
964 
965  ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.", table);
966 
967  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
968  * An integer greater than zero indicates the number of rows affected
969  * Zero indicates that no records were updated
970  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
971  */
972 
973  if (numrows >= 0) {
974  return numrows;
975  }
976 
977  return -1;
978 }
979 
980 static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
981 {
982  RAII_VAR(PGresult *, result, NULL, PQclear);
983  int numrows = 0;
984  int pgresult;
985  struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
987  const char *newparam, *newval;
988 
989  /*
990  * Ignore database from the extconfig.conf since it was
991  * configured by res_pgsql.conf.
992  */
993  database = dbname;
994 
995  if (!table) {
996  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
997  return -1;
998  }
999 
1000  /* Get the first parameter and first value in our list of passed paramater/value pairs */
1001  /*newparam = va_arg(ap, const char *);
1002  newval = va_arg(ap, const char *);
1003  if (!newparam || !newval) {*/
1004  if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
1006  "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
1007  if (pgsqlConn) {
1008  PQfinish(pgsqlConn);
1009  pgsqlConn = NULL;
1010  };
1011  return -1;
1012  }
1013 
1014  /* Must connect to the server before anything else, as the escape function requires the connection handle.. */
1016  if (!pgsql_reconnect(database)) {
1018  return -1;
1019  }
1020 
1021 
1022  /* Create the first part of the query using the first parameter/value pairs we just extracted
1023  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1024 
1025  ESCAPE_STRING(buf1, keyfield);
1026  ESCAPE_STRING(buf2, lookup);
1027  ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
1028  while ((newparam = va_arg(ap, const char *))) {
1029  newval = va_arg(ap, const char *);
1030  ESCAPE_STRING(buf1, newparam);
1031  ESCAPE_STRING(buf2, newval);
1032  ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
1033  }
1034 
1035  ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
1036 
1037  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
1039  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
1040  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
1041  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
1043  return -1;
1044  } else {
1045  ExecStatusType result_status = PQresultStatus(result);
1046  if (result_status != PGRES_COMMAND_OK
1047  && result_status != PGRES_TUPLES_OK
1048  && result_status != PGRES_NONFATAL_ERROR) {
1050  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
1051  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
1052  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
1053  PQresultErrorMessage(result), PQresStatus(result_status));
1055  return -1;
1056  }
1057  }
1058 
1059  numrows = atoi(PQcmdTuples(result));
1061 
1062  ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
1063 
1064  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1065  * An integer greater than zero indicates the number of rows affected
1066  * Zero indicates that no records were updated
1067  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1068  */
1069 
1070  if (numrows >= 0)
1071  return (int) numrows;
1072 
1073  return -1;
1074 }
1075 
1076 
1077 static struct ast_config *config_pgsql(const char *database, const char *table,
1078  const char *file, struct ast_config *cfg,
1079  struct ast_flags flags, const char *suggested_incl, const char *who_asked)
1080 {
1081  RAII_VAR(PGresult *, result, NULL, PQclear);
1082  long num_rows;
1083  struct ast_variable *new_v;
1084  struct ast_category *cur_cat = NULL;
1085  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
1086  char last[80];
1087  int last_cat_metric = 0;
1088 
1089  last[0] = '\0';
1090 
1091  /*
1092  * Ignore database from the extconfig.conf since it is
1093  * configured by res_pgsql.conf.
1094  */
1095  database = dbname;
1096 
1097  if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
1098  ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
1099  return NULL;
1100  }
1101 
1102  ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
1103  "WHERE filename='%s' and commented=0"
1104  "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
1105 
1106  ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
1107 
1108  /* We now have our complete statement; Lets connect to the server and execute it. */
1110  if (!pgsql_reconnect(database)) {
1112  return NULL;
1113  }
1114 
1115  if (!(result = PQexec(pgsqlConn, ast_str_buffer(sql)))) {
1117  "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", table, database);
1118  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
1119  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn));
1121  return NULL;
1122  } else {
1123  ExecStatusType result_status = PQresultStatus(result);
1124  if (result_status != PGRES_COMMAND_OK
1125  && result_status != PGRES_TUPLES_OK
1126  && result_status != PGRES_NONFATAL_ERROR) {
1128  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
1129  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
1130  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
1131  PQresultErrorMessage(result), PQresStatus(result_status));
1133  return NULL;
1134  }
1135  }
1136 
1137  if ((num_rows = PQntuples(result)) > 0) {
1138  int rowIndex = 0;
1139 
1140  ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
1141 
1142  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
1143  char *field_category = PQgetvalue(result, rowIndex, 0);
1144  char *field_var_name = PQgetvalue(result, rowIndex, 1);
1145  char *field_var_val = PQgetvalue(result, rowIndex, 2);
1146  char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
1147  if (!strcmp(field_var_name, "#include")) {
1148  if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
1150  return NULL;
1151  }
1152  continue;
1153  }
1154 
1155  if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
1156  cur_cat = ast_category_new(field_category, "", 99999);
1157  if (!cur_cat)
1158  break;
1159  ast_copy_string(last, field_category, sizeof(last));
1160  last_cat_metric = atoi(field_cat_metric);
1161  ast_category_append(cfg, cur_cat);
1162  }
1163  new_v = ast_variable_new(field_var_name, field_var_val, "");
1164  ast_variable_append(cur_cat, new_v);
1165  }
1166  } else {
1168  "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
1169  }
1170 
1172 
1173  return cfg;
1174 }
1175 
1176 static int require_pgsql(const char *database, const char *tablename, va_list ap)
1177 {
1178  struct columns *column;
1179  struct tables *table;
1180  char *elm;
1181  int type, size, res = 0;
1182 
1183  /*
1184  * Ignore database from the extconfig.conf since it was
1185  * configured by res_pgsql.conf.
1186  */
1187  database = dbname;
1188 
1189  table = find_table(database, tablename);
1190  if (!table) {
1191  ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
1192  return -1;
1193  }
1194 
1195  while ((elm = va_arg(ap, char *))) {
1196  type = va_arg(ap, require_type);
1197  size = va_arg(ap, int);
1198  AST_LIST_TRAVERSE(&table->columns, column, list) {
1199  if (strcmp(column->name, elm) == 0) {
1200  /* Char can hold anything, as long as it is large enough */
1201  if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
1202  if ((size > column->len) && column->len != -1) {
1203  ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
1204  res = -1;
1205  }
1206  } else if (strncmp(column->type, "int", 3) == 0) {
1207  int typesize = atoi(column->type + 3);
1208  /* Integers can hold only other integers */
1209  if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
1210  type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
1211  type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
1212  type == RQ_UINTEGER2) && typesize == 2) {
1213  ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
1214  res = -1;
1215  } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
1216  type == RQ_UINTEGER4) && typesize == 4) {
1217  ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
1218  res = -1;
1219  } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
1220  ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
1221  column->name,
1222  type == RQ_CHAR ? "char" :
1223  type == RQ_DATETIME ? "datetime" :
1224  type == RQ_DATE ? "date" :
1225  type == RQ_FLOAT ? "float" :
1226  "a rather stiff drink ",
1227  size, column->type);
1228  res = -1;
1229  }
1230  } else if (strncmp(column->type, "float", 5) == 0) {
1231  if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1232  ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
1233  res = -1;
1234  }
1235  } else if (strncmp(column->type, "timestamp", 9) == 0) {
1236  if (type != RQ_DATETIME && type != RQ_DATE) {
1237  ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
1238  res = -1;
1239  }
1240  } else { /* There are other types that no module implements yet */
1241  ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
1242  res = -1;
1243  }
1244  break;
1245  }
1246  }
1247 
1248  if (!column) {
1249  if (requirements == RQ_WARN) {
1250  ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
1251  } else {
1252  struct ast_str *sql = ast_str_create(100);
1253  char fieldtype[15];
1254  PGresult *result;
1255 
1256  if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
1257  /* Size is minimum length; make it at least 50% greater,
1258  * just to be sure, because PostgreSQL doesn't support
1259  * resizing columns. */
1260  snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
1261  size < 15 ? size * 2 :
1262  (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
1263  } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
1264  snprintf(fieldtype, sizeof(fieldtype), "INT2");
1265  } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
1266  snprintf(fieldtype, sizeof(fieldtype), "INT4");
1267  } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
1268  snprintf(fieldtype, sizeof(fieldtype), "INT8");
1269  } else if (type == RQ_UINTEGER8) {
1270  /* No such type on PostgreSQL */
1271  snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
1272  } else if (type == RQ_FLOAT) {
1273  snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
1274  } else if (type == RQ_DATE) {
1275  snprintf(fieldtype, sizeof(fieldtype), "DATE");
1276  } else if (type == RQ_DATETIME) {
1277  snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
1278  } else {
1279  ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
1280  ast_free(sql);
1281  continue;
1282  }
1283  ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
1284  ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
1285 
1287  if (!pgsql_reconnect(database)) {
1289  ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
1290  ast_free(sql);
1291  continue;
1292  }
1293 
1294  ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
1295  result = PQexec(pgsqlConn, ast_str_buffer(sql));
1296  ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
1297  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
1298  ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
1299  }
1300  PQclear(result);
1302 
1303  ast_free(sql);
1304  }
1305  }
1306  }
1307  release_table(table);
1308  return res;
1309 }
1310 
1311 static int unload_pgsql(const char *database, const char *tablename)
1312 {
1313  struct tables *cur;
1314 
1315  /*
1316  * Ignore database from the extconfig.conf since it was
1317  * configured by res_pgsql.conf.
1318  */
1319  database = dbname;
1320 
1321  ast_debug(2, "About to lock table cache list\n");
1323  ast_debug(2, "About to traverse table cache list\n");
1325  if (strcmp(cur->name, tablename) == 0) {
1326  ast_debug(2, "About to remove matching cache entry\n");
1328  ast_debug(2, "About to destroy matching cache entry\n");
1329  destroy_table(cur);
1330  ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
1331  break;
1332  }
1333  }
1336  ast_debug(2, "About to return\n");
1337  return cur ? 0 : -1;
1338 }
1339 
1341  .name = "pgsql",
1342  .load_func = config_pgsql,
1343  .realtime_func = realtime_pgsql,
1344  .realtime_multi_func = realtime_multi_pgsql,
1345  .store_func = store_pgsql,
1346  .destroy_func = destroy_pgsql,
1347  .update_func = update_pgsql,
1348  .update2_func = update2_pgsql,
1349  .require_func = require_pgsql,
1350  .unload_func = unload_pgsql,
1351 };
1352 
1353 static int load_module(void)
1354 {
1355  if(!parse_config(0))
1356  return AST_MODULE_LOAD_DECLINE;
1357 
1359  ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
1361 
1362  return 0;
1363 }
1364 
1365 static int unload_module(void)
1366 {
1367  struct tables *table;
1368  /* Acquire control before doing anything to the module itself. */
1370 
1371  if (pgsqlConn) {
1372  PQfinish(pgsqlConn);
1373  pgsqlConn = NULL;
1374  }
1377  ast_verb(1, "PostgreSQL RealTime unloaded.\n");
1378 
1379  /* Destroy cached table info */
1381  while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
1382  destroy_table(table);
1383  }
1385 
1386  /* Unlock so something else can destroy the lock. */
1388 
1389  return 0;
1390 }
1391 
1392 static int reload(void)
1393 {
1394  parse_config(1);
1395 
1396  return 0;
1397 }
1398 
1399 static int parse_config(int is_reload)
1400 {
1401  struct ast_config *config;
1402  const char *s;
1403  struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1404 
1405  config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
1406  if (config == CONFIG_STATUS_FILEUNCHANGED) {
1407  return 0;
1408  }
1409 
1410  if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
1411  ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
1412  return 0;
1413  }
1414 
1416 
1417  if (pgsqlConn) {
1418  PQfinish(pgsqlConn);
1419  pgsqlConn = NULL;
1420  }
1421 
1422  if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
1424  "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
1425  strcpy(dbuser, "asterisk");
1426  } else {
1427  ast_copy_string(dbuser, s, sizeof(dbuser));
1428  }
1429 
1430  if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
1432  "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
1433  strcpy(dbpass, "asterisk");
1434  } else {
1435  ast_copy_string(dbpass, s, sizeof(dbpass));
1436  }
1437 
1438  if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
1440  "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
1441  dbhost[0] = '\0';
1442  } else {
1443  ast_copy_string(dbhost, s, sizeof(dbhost));
1444  }
1445 
1446  if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
1448  "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
1449  strcpy(dbname, "asterisk");
1450  } else {
1451  ast_copy_string(dbname, s, sizeof(dbname));
1452  }
1453 
1454  if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
1456  "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
1457  dbport = 5432;
1458  } else {
1459  dbport = atoi(s);
1460  }
1461 
1462  if (!ast_strlen_zero(dbhost)) {
1463  /* No socket needed */
1464  } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
1466  "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
1467  strcpy(dbsock, "/tmp");
1468  } else {
1469  ast_copy_string(dbsock, s, sizeof(dbsock));
1470  }
1471 
1472  if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
1474  "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
1476  } else if (!strcasecmp(s, "createclose")) {
1478  } else if (!strcasecmp(s, "createchar")) {
1480  }
1481 
1482  ast_config_destroy(config);
1483 
1484  if (option_debug) {
1485  if (!ast_strlen_zero(dbhost)) {
1486  ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
1487  ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
1488  } else {
1489  ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
1490  }
1491  ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
1492  ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
1493  ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
1494  }
1495 
1496  if (!pgsql_reconnect(NULL)) {
1498  "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
1499  ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
1500  }
1501 
1502  ast_verb(2, "PostgreSQL RealTime reloaded.\n");
1503 
1504  /* Done reloading. Release lock so others can now use driver. */
1506 
1507  return 1;
1508 }
1509 
1510 static int pgsql_reconnect(const char *database)
1511 {
1512  char my_database[50];
1513 
1514  ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
1515 
1516  /* mutex lock should have been locked before calling this function. */
1517 
1518  if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
1519  PQfinish(pgsqlConn);
1520  pgsqlConn = NULL;
1521  }
1522 
1523  /* DB password can legitimately be 0-length */
1524  if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
1525  struct ast_str *connInfo = ast_str_create(128);
1526 
1527  ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
1528  S_OR(dbhost, dbsock), dbport, my_database, dbuser);
1529  if (!ast_strlen_zero(dbpass))
1530  ast_str_append(&connInfo, 0, " password=%s", dbpass);
1531 
1532  ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
1533  pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
1534  ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
1535  ast_free(connInfo);
1536  connInfo = NULL;
1537 
1538  ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
1539  if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
1540  ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
1541  connect_time = time(NULL);
1542  version = PQserverVersion(pgsqlConn);
1543  return 1;
1544  } else {
1546  "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
1547  my_database, dbhost, PQresultErrorMessage(NULL));
1548  return 0;
1549  }
1550  } else {
1551  ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
1552  return 1;
1553  }
1554 }
1555 
1556 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1557 {
1558  struct tables *cur;
1559  int l, which;
1560  char *ret = NULL;
1561 
1562  switch (cmd) {
1563  case CLI_INIT:
1564  e->command = "realtime show pgsql cache";
1565  e->usage =
1566  "Usage: realtime show pgsql cache [<table>]\n"
1567  " Shows table cache for the PostgreSQL RealTime driver\n";
1568  return NULL;
1569  case CLI_GENERATE:
1570  if (a->argc != 4) {
1571  return NULL;
1572  }
1573  l = strlen(a->word);
1574  which = 0;
1577  if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
1578  ret = ast_strdup(cur->name);
1579  break;
1580  }
1581  }
1583  return ret;
1584  }
1585 
1586  if (a->argc == 4) {
1587  /* List of tables */
1590  ast_cli(a->fd, "%s\n", cur->name);
1591  }
1593  } else if (a->argc == 5) {
1594  /* List of columns */
1595  if ((cur = find_table(NULL, a->argv[4]))) {
1596  struct columns *col;
1597  ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
1598  ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
1599  AST_LIST_TRAVERSE(&cur->columns, col, list) {
1600  ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
1601  }
1602  release_table(cur);
1603  } else {
1604  ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
1605  }
1606  }
1607  return 0;
1608 }
1609 
1610 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1611 {
1612  char status[256], credentials[100] = "";
1613  int ctimesec = time(NULL) - connect_time;
1614 
1615  switch (cmd) {
1616  case CLI_INIT:
1617  e->command = "realtime show pgsql status";
1618  e->usage =
1619  "Usage: realtime show pgsql status\n"
1620  " Shows connection information for the PostgreSQL RealTime driver\n";
1621  return NULL;
1622  case CLI_GENERATE:
1623  return NULL;
1624  }
1625 
1626  if (a->argc != 4)
1627  return CLI_SHOWUSAGE;
1628 
1629  if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
1630  if (!ast_strlen_zero(dbhost))
1631  snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
1632  else if (!ast_strlen_zero(dbsock))
1633  snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
1634  else
1635  snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
1636 
1637  if (!ast_strlen_zero(dbuser))
1638  snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
1639 
1640  if (ctimesec > 31536000)
1641  ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
1642  status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
1643  (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
1644  else if (ctimesec > 86400)
1645  ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
1646  credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
1647  ctimesec % 60);
1648  else if (ctimesec > 3600)
1649  ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
1650  ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
1651  else if (ctimesec > 60)
1652  ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
1653  ctimesec % 60);
1654  else
1655  ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
1656 
1657  return CLI_SUCCESS;
1658  } else {
1659  return CLI_FAILURE;
1660  }
1661 }
1662 
1663 /* needs usecount semantics defined */
1664 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
1665  .load = load_module,
1666  .unload = unload_module,
1667  .reload = reload,
1668  .load_pri = AST_MODPRI_REALTIME_DRIVER,
1669  );
unsigned int hasdefault
Definition: cdr_pgsql.c:69
#define has_schema_support
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
static struct ast_config * config_pgsql(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl, const char *who_asked)
#define ast_rwlock_rdlock(a)
Definition: lock.h:201
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
uint32_t version
static void destroy_table(struct tables *table)
Asterisk locking-related definitions:
static int update_pgsql(const char *database, const char *tablename, const char *keyfield, const char *lookup, va_list ap)
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
static const char config[]
Definition: cdr_csv.c:57
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
char * strsep(char **str, const char *delims)
#define ast_strdup(a)
Definition: astmm.h:109
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
static int dbport
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
int option_debug
Definition: asterisk.c:182
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
#define ast_rwlock_destroy(rwlock)
Definition: lock.h:199
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:482
static char * handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char dbname[MAX_DB_OPTION_SIZE]
#define release_table(table)
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
static int unload_pgsql(const char *database, const char *tablename)
#define var
Definition: ast_expr2f.c:606
Definition: cli.h:146
Configuration File Parser.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
static struct ast_threadstorage buf2
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#define ast_mutex_lock(a)
Definition: lock.h:155
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static ast_mutex_t pgsql_lock
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
#define ast_rwlock_unlock(a)
Definition: lock.h:200
static char dbpass[MAX_DB_OPTION_SIZE]
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
char * table
static struct tables * find_table(const char *database, const char *orig_tablename)
char name[0]
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
#define ESCAPE_STRING(buffer, stringname)
Configuration engine structure, used to define realtime drivers.
Definition: config.h:121
Utility functions.
SQLSMALLINT type
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category structure.
Definition: config.c:673
#define CONFIG_STATUS_FILEMISSING
Definition: config.h:50
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
struct tables::@329 list
Definition: config.h:68
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
#define RES_CONFIG_PGSQL_CONF
General Asterisk PBX channel definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:915
const int fd
Definition: cli.h:153
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
static char * handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static struct ast_threadstorage where_buf
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
struct sla_ringing_trunk * last
Definition: app_meetme.c:965
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:155
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
char * ast_realtime_decode_chunk(char *chunk)
Remove standard encoding from realtime values, which ensures that a semicolon embedded within a singl...
Definition: config.c:2773
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
static int pgsql_reconnect(const char *database)
static int store_pgsql(const char *database, const char *table, va_list ap)
Core PBX routines and definitions.
static struct ast_config * realtime_multi_pgsql(const char *database, const char *table, va_list ap)
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
struct tables::odbc_columns columns
const char *const * argv
Definition: cli.h:155
static struct ast_cli_entry cli_realtime[]
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:224
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static int update2_pgsql(const char *database, const char *tablename, va_list ap)
struct columns::@71 list
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
#define CLI_SHOWUSAGE
Definition: cli.h:44
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: config.c:483
#define ast_rwlock_init(rwlock)
wrapper for rwlock with tracking enabled
Definition: lock.h:190
struct sla_ringing_trunk * first
Definition: app_meetme.c:965
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
ast_rwlock_t lock
#define LOG_NOTICE
Definition: logger.h:133
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
Definition: config.c:2378
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static struct ast_threadstorage semibuf_buf
static struct ast_threadstorage buf1
#define CLI_FAILURE
Definition: cli.h:45
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: config.c:888
static struct ast_threadstorage findtable_buf
static int require_pgsql(const char *database, const char *tablename, va_list ap)
const char * word
Definition: cli.h:157
static enum @327 requirements
static const char type[]
Definition: chan_nbs.c:57
Structure used to handle boolean flags.
Definition: utils.h:200
static struct ast_threadstorage sql_buf
static char dbuser[MAX_DB_OPTION_SIZE]
const char * usage
Definition: cli.h:171
#define ast_rwlock_wrlock(a)
Definition: lock.h:202
#define CLI_SUCCESS
Definition: cli.h:43
static struct ast_variable * realtime_pgsql(const char *database, const char *tablename, va_list ap)
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:666
unsigned int notnull
Definition: cdr_pgsql.c:68
int len
Definition: cdr_pgsql.c:67
Structure for rwlock and tracking information.
Definition: lock.h:133
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
int ast_rq_is_int(require_type type)
Check if require type is an integer type.
Definition: config.h:768
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
static struct columns * find_column(struct tables *t, const char *colname)
static char dbsock[MAX_DB_OPTION_SIZE]
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
static int parse_config(int reload)
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:669
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Definition: config.c:719
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
struct ast_config * ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl_file, const char *who_asked)
Definition: config.c:2459
static struct ast_config_engine pgsql_engine
static time_t connect_time
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: config.c:867
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static char dbhost[MAX_DB_OPTION_SIZE]
Asterisk module definitions.
require_type
Types used in ast_realtime_require_field.
Definition: config.h:57
static int load_module(void)
Definition: config.h:70
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:526
struct ast_variable * ast_variable_new(const char *name, const char *value, const char *filename)
Definition: config.c:278
#define MAX_DB_OPTION_SIZE
static int unload_module(void)
jack_status_t status
Definition: app_jack.c:143
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
static int reload(void)
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: config.c:2397
#define ast_mutex_unlock(a)
Definition: lock.h:156
static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)