Asterisk - The Open Source Telephony Project  18.5.0
res_config_pgsql.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2017, Digium, Inc.
5  *
6  * Manuel Guesdon <[email protected]> - PostgreSQL RealTime Driver Author/Adaptor
7  * Mark Spencer <[email protected]> - Asterisk Author
8  * Matthew Boehm <[email protected]> - 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 <[email protected]>
20  * \author Manuel Guesdon <[email protected]> - PostgreSQL RealTime Driver Author/Adaptor
21  *
22  * 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 #include <libpq-fe.h> /* PostgreSQL */
33 
34 #include "asterisk/file.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/utils.h"
41 #include "asterisk/cli.h"
42 
49 
50 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
51 
52 static PGconn *pgsqlConn = NULL;
53 static int version;
54 #define has_schema_support (version > 70300 ? 1 : 0)
55 #define USE_BACKSLASH_AS_STRING (version >= 90100 ? 1 : 0)
56 
57 #define MAX_DB_OPTION_SIZE 64
58 
59 struct columns {
60  char *name;
61  char *type;
62  int len;
63  unsigned int notnull:1;
64  unsigned int hasdefault:1;
66 };
67 
68 struct tables {
72  char name[0];
73 };
74 
76 
77 static char dbhost[MAX_DB_OPTION_SIZE] = "";
78 static char dbuser[MAX_DB_OPTION_SIZE] = "";
79 static char dbpass[MAX_DB_OPTION_SIZE] = "";
80 static char dbname[MAX_DB_OPTION_SIZE] = "";
81 static char dbappname[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 /*! \brief Helper function for pgsql_exec. For running querys, use pgsql_exec()
130  *
131  * Connect if not currently connected. Run the given query.
132  *
133  * \param database database name we are connected to (used for error logging)
134  * \param tablename table name we are connected to (used for error logging)
135  * \param sql sql query string to execute
136  * \param result pointer for where to store the result handle
137  *
138  * \return -1 on fatal query error
139  * \return -2 on query failure that resulted in disconnection
140  * \return 0 on success
141  *
142  * \note see pgsql_exec for full example
143  */
144 static int _pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
145 {
146  ExecStatusType result_status;
147 
148  if (!pgsqlConn) {
149  ast_debug(1, "PostgreSQL connection not defined, connecting\n");
150 
151  if (pgsql_reconnect(database) != 1) {
152  ast_log(LOG_NOTICE, "reconnect failed\n");
153  *result = NULL;
154  return -1;
155  }
156 
157  ast_debug(1, "PostgreSQL connection successful\n");
158  }
159 
160  *result = PQexec(pgsqlConn, sql);
161  result_status = PQresultStatus(*result);
162  if (result_status != PGRES_COMMAND_OK
163  && result_status != PGRES_TUPLES_OK
164  && result_status != PGRES_NONFATAL_ERROR) {
165 
166  ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to query '%s@%s'.\n", tablename, database);
167  ast_log(LOG_ERROR, "PostgreSQL RealTime: Query Failed: %s\n", sql);
168  ast_log(LOG_ERROR, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
169  PQresultErrorMessage(*result),
170  PQresStatus(result_status));
171 
172  /* we may have tried to run a command on a disconnected/disconnecting handle */
173  /* are we no longer connected to the database... if not try again */
174  if (PQstatus(pgsqlConn) != CONNECTION_OK) {
175  PQfinish(pgsqlConn);
176  pgsqlConn = NULL;
177  return -2;
178  }
179 
180  /* connection still okay, which means the query is just plain bad */
181  return -1;
182  }
183 
184  ast_debug(1, "PostgreSQL query successful: %s\n", sql);
185  return 0;
186 }
187 
188 /*! \brief Do a postgres query, with reconnection support
189  *
190  * Connect if not currently connected. Run the given query
191  * and if we're disconnected afterwards, reconnect and query again.
192  *
193  * \param database database name we are connected to (used for error logging)
194  * \param tablename table name we are connected to (used for error logging)
195  * \param sql sql query string to execute
196  * \param result pointer for where to store the result handle
197  *
198  * \return -1 on query failure
199  * \return 0 on success
200  *
201  * \code
202  * int i, rows;
203  * PGresult *result;
204  * char *field_name, *field_type, *field_len, *field_notnull, *field_default;
205  *
206  * pgsql_exec("db", "table", "SELECT 1", &result)
207  *
208  * rows = PQntuples(result);
209  * for (i = 0; i < rows; i++) {
210  * field_name = PQgetvalue(result, i, 0);
211  * field_type = PQgetvalue(result, i, 1);
212  * field_len = PQgetvalue(result, i, 2);
213  * field_notnull = PQgetvalue(result, i, 3);
214  * field_default = PQgetvalue(result, i, 4);
215  * }
216  * \endcode
217  */
218 static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
219 {
220  int attempts = 0;
221  int res;
222 
223  /* Try the query, note failure if any */
224  /* On first failure, reconnect and try again (_pgsql_exec handles reconnect) */
225  /* On second failure, treat as fatal query error */
226 
227  while (attempts++ < 2) {
228  ast_debug(1, "PostgreSQL query attempt %d\n", attempts);
229  res = _pgsql_exec(database, tablename, sql, result);
230 
231  if (res == 0) {
232  if (attempts > 1) {
233  ast_log(LOG_NOTICE, "PostgreSQL RealTime: Query finally succeeded: %s\n", sql);
234  }
235 
236  return 0;
237  }
238 
239  if (res == -1) {
240  return -1; /* Still connected to db, but could not process query (fatal error) */
241  }
242 
243  /* res == -2 (query on a disconnected handle) */
244  ast_debug(1, "PostgreSQL query attempt %d failed, trying again\n", attempts);
245  }
246 
247  return -1;
248 }
249 
250 static struct tables *find_table(const char *database, const char *orig_tablename)
251 {
252  struct columns *column;
253  struct tables *table;
254  struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
255  RAII_VAR(PGresult *, result, NULL, PQclear);
256  int exec_result;
257  char *fname, *ftype, *flen, *fnotnull, *fdef;
258  int i, rows;
259 
262  if (!strcasecmp(table->name, orig_tablename)) {
263  ast_debug(1, "Found table in cache; now locking\n");
264  ast_rwlock_rdlock(&table->lock);
265  ast_debug(1, "Lock cached table; now returning\n");
267  return table;
268  }
269  }
270 
271  if (database == NULL) {
273  return NULL;
274  }
275 
276  ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
277 
278  /* Not found, scan the table */
279  if (has_schema_support) {
280  char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
281  if (strchr(orig_tablename, '.')) {
282  tmp_schemaname = ast_strdupa(orig_tablename);
283  tmp_tablename = strchr(tmp_schemaname, '.');
284  *tmp_tablename++ = '\0';
285  } else {
286  tmp_schemaname = "";
287  tmp_tablename = ast_strdupa(orig_tablename);
288  }
289 
290  tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
291  PQescapeStringConn(pgsqlConn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
292  schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
293  PQescapeStringConn(pgsqlConn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
294 
295  ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, pg_catalog.pg_get_expr(d.adbin, d.adrelid) 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",
296  tablename,
297  ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
298  } else {
299  char *tablename;
300  tablename = ast_alloca(strlen(orig_tablename) * 2 + 1);
301  PQescapeStringConn(pgsqlConn, tablename, orig_tablename, strlen(orig_tablename), NULL);
302 
303  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", tablename);
304  }
305 
307  exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
309  ast_debug(1, "Query of table structure complete. Now retrieving results.\n");
310  if (exec_result != 0) {
311  ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
313  return NULL;
314  }
315 
316  if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
317  ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
319  return NULL;
320  }
321  strcpy(table->name, orig_tablename); /* SAFE */
322  ast_rwlock_init(&table->lock);
324 
325  rows = PQntuples(result);
326  for (i = 0; i < rows; i++) {
327  fname = PQgetvalue(result, i, 0);
328  ftype = PQgetvalue(result, i, 1);
329  flen = PQgetvalue(result, i, 2);
330  fnotnull = PQgetvalue(result, i, 3);
331  fdef = PQgetvalue(result, i, 4);
332  ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
333 
334  if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
335  ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
336  destroy_table(table);
338  return NULL;
339  }
340 
341  if (strcmp(flen, "-1") == 0) {
342  /* Some types, like chars, have the length stored in a different field */
343  flen = PQgetvalue(result, i, 5);
344  sscanf(flen, "%30d", &column->len);
345  column->len -= 4;
346  } else {
347  sscanf(flen, "%30d", &column->len);
348  }
349  column->name = (char *)column + sizeof(*column);
350  column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
351  strcpy(column->name, fname);
352  strcpy(column->type, ftype);
353  if (*fnotnull == 't') {
354  column->notnull = 1;
355  } else {
356  column->notnull = 0;
357  }
358  if (!ast_strlen_zero(fdef)) {
359  column->hasdefault = 1;
360  } else {
361  column->hasdefault = 0;
362  }
363  AST_LIST_INSERT_TAIL(&table->columns, column, list);
364  }
365 
367  ast_rwlock_rdlock(&table->lock);
369  return table;
370 }
371 
372 #define release_table(table) ast_rwlock_unlock(&(table)->lock);
373 
374 static struct columns *find_column(struct tables *t, const char *colname)
375 {
376  struct columns *column;
377 
378  /* Check that the column exists in the table */
379  AST_LIST_TRAVERSE(&t->columns, column, list) {
380  if (strcmp(column->name, colname) == 0) {
381  return column;
382  }
383  }
384  return NULL;
385 }
386 
387 #define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE"))
388 #define ESCAPE_CLAUSE (USE_BACKSLASH_AS_STRING ? " ESCAPE '\\'" : " ESCAPE '\\\\'")
389 
390 static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, const struct ast_variable *fields)
391 {
392  RAII_VAR(PGresult *, result, NULL, PQclear);
393  int pgresult;
394  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
395  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
396  char *stringp;
397  char *chunk;
398  char *op;
399  char *escape = "";
400  const struct ast_variable *field = fields;
401  struct ast_variable *var = NULL, *prev = NULL;
402 
403  /*
404  * Ignore database from the extconfig.conf since it was
405  * configured by res_pgsql.conf.
406  */
407  database = dbname;
408 
409  if (!tablename) {
410  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
411  return NULL;
412  }
413 
414  /*
415  * Must connect to the server before anything else as ESCAPE_STRING()
416  * uses pgsqlConn
417  */
419  if (!pgsql_reconnect(database)) {
421  return NULL;
422  }
423 
424  /* Get the first parameter and first value in our list of passed paramater/value pairs */
425  if (!field) {
427  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
428  if (pgsqlConn) {
429  PQfinish(pgsqlConn);
430  pgsqlConn = NULL;
431  }
433  return NULL;
434  }
435 
436  /* Create the first part of the query using the first parameter/value pairs we just extracted
437  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
438  if (!strchr(field->name, ' ')) {
439  op = " =";
440  } else {
441  op = "";
442  if (IS_SQL_LIKE_CLAUSE(field->name)) {
443  escape = ESCAPE_CLAUSE;
444  }
445  }
446 
447  ESCAPE_STRING(escapebuf, field->value);
448  if (pgresult) {
449  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
451  return NULL;
452  }
453 
454  ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", tablename, field->name, op, ast_str_buffer(escapebuf), escape);
455  while ((field = field->next)) {
456  escape = "";
457  if (!strchr(field->name, ' ')) {
458  op = " =";
459  } else {
460  op = "";
461  if (IS_SQL_LIKE_CLAUSE(field->name)) {
462  escape = ESCAPE_CLAUSE;
463  }
464  }
465 
466  ESCAPE_STRING(escapebuf, field->value);
467  if (pgresult) {
468  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
470  return NULL;
471  }
472 
473  ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
474  }
475  ast_str_append(&sql, 0, " LIMIT 1");
476 
477  /* We now have our complete statement; Lets connect to the server and execute it. */
478  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
480  return NULL;
481  }
482 
483  ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
484 
485  if (PQntuples(result) > 0) {
486  int i = 0;
487  int numFields = PQnfields(result);
488  char **fieldnames = NULL;
489 
490  ast_debug(1, "PostgreSQL RealTime: Found a row.\n");
491 
492  if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
494  return NULL;
495  }
496  for (i = 0; i < numFields; i++)
497  fieldnames[i] = PQfname(result, i);
498  for (i = 0; i < numFields; i++) {
499  stringp = PQgetvalue(result, 0, i);
500  while (stringp) {
501  chunk = strsep(&stringp, ";");
502  if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
503  if (prev) {
504  prev->next = ast_variable_new(fieldnames[i], chunk, "");
505  if (prev->next) {
506  prev = prev->next;
507  }
508  } else {
509  prev = var = ast_variable_new(fieldnames[i], chunk, "");
510  }
511  }
512  }
513  }
514  ast_free(fieldnames);
515  } else {
516  ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
517  }
518 
520 
521  return var;
522 }
523 
524 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, const struct ast_variable *fields)
525 {
526  RAII_VAR(PGresult *, result, NULL, PQclear);
527  int num_rows = 0, pgresult;
528  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
529  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
530  const struct ast_variable *field = fields;
531  const char *initfield = NULL;
532  char *stringp;
533  char *chunk;
534  char *op;
535  char *escape = "";
536  struct ast_variable *var = NULL;
537  struct ast_config *cfg = NULL;
538  struct ast_category *cat = NULL;
539 
540  /*
541  * Ignore database from the extconfig.conf since it was
542  * configured by res_pgsql.conf.
543  */
544  database = dbname;
545 
546  if (!table) {
547  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
548  return NULL;
549  }
550 
551  if (!(cfg = ast_config_new()))
552  return NULL;
553 
554  /*
555  * Must connect to the server before anything else as ESCAPE_STRING()
556  * uses pgsqlConn
557  */
559  if (!pgsql_reconnect(database)) {
561  return NULL;
562  }
563 
564  /* Get the first parameter and first value in our list of passed paramater/value pairs */
565  if (!field) {
567  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
568  if (pgsqlConn) {
569  PQfinish(pgsqlConn);
570  pgsqlConn = NULL;
571  }
573  ast_config_destroy(cfg);
574  return NULL;
575  }
576 
577  initfield = ast_strdupa(field->name);
578  if ((op = strchr(initfield, ' '))) {
579  *op = '\0';
580  }
581 
582  /* Create the first part of the query using the first parameter/value pairs we just extracted
583  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
584 
585  if (!strchr(field->name, ' ')) {
586  op = " =";
587  escape = "";
588  } else {
589  op = "";
590  if (IS_SQL_LIKE_CLAUSE(field->name)) {
591  escape = ESCAPE_CLAUSE;
592  }
593  }
594 
595  ESCAPE_STRING(escapebuf, field->value);
596  if (pgresult) {
597  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
599  ast_config_destroy(cfg);
600  return NULL;
601  }
602 
603  ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(escapebuf), escape);
604  while ((field = field->next)) {
605  escape = "";
606  if (!strchr(field->name, ' ')) {
607  op = " =";
608  escape = "";
609  } else {
610  op = "";
611  if (IS_SQL_LIKE_CLAUSE(field->name)) {
612  escape = ESCAPE_CLAUSE;
613  }
614  }
615 
616  ESCAPE_STRING(escapebuf, field->value);
617  if (pgresult) {
618  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
620  ast_config_destroy(cfg);
621  return NULL;
622  }
623 
624  ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape);
625  }
626 
627  if (initfield) {
628  ast_str_append(&sql, 0, " ORDER BY %s", initfield);
629  }
630 
631  /* We now have our complete statement; Lets connect to the server and execute it. */
632  if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
634  ast_config_destroy(cfg);
635  return NULL;
636  } else {
637  ExecStatusType result_status = PQresultStatus(result);
638  if (result_status != PGRES_COMMAND_OK
639  && result_status != PGRES_TUPLES_OK
640  && result_status != PGRES_NONFATAL_ERROR) {
642  "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
643  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
644  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
645  PQresultErrorMessage(result), PQresStatus(result_status));
647  ast_config_destroy(cfg);
648  return NULL;
649  }
650  }
651 
652  ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
653 
654  if ((num_rows = PQntuples(result)) > 0) {
655  int numFields = PQnfields(result);
656  int i = 0;
657  int rowIndex = 0;
658  char **fieldnames = NULL;
659 
660  ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
661 
662  if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
664  ast_config_destroy(cfg);
665  return NULL;
666  }
667  for (i = 0; i < numFields; i++)
668  fieldnames[i] = PQfname(result, i);
669 
670  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
671  var = NULL;
673  if (!cat) {
674  continue;
675  }
676  for (i = 0; i < numFields; i++) {
677  stringp = PQgetvalue(result, rowIndex, i);
678  while (stringp) {
679  chunk = strsep(&stringp, ";");
680  if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
681  if (initfield && !strcmp(initfield, fieldnames[i])) {
682  ast_category_rename(cat, chunk);
683  }
684  var = ast_variable_new(fieldnames[i], chunk, "");
685  ast_variable_append(cat, var);
686  }
687  }
688  }
689  ast_category_append(cfg, cat);
690  }
691  ast_free(fieldnames);
692  } else {
693  ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
694  }
695 
697 
698  return cfg;
699 }
700 
701 static int update_pgsql(const char *database, const char *tablename, const char *keyfield,
702  const char *lookup, const struct ast_variable *fields)
703 {
704  RAII_VAR(PGresult *, result, NULL, PQclear);
705  int numrows = 0, pgresult;
706  const struct ast_variable *field = fields;
707  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
708  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
709  struct tables *table;
710  struct columns *column = NULL;
711 
712  /*
713  * Ignore database from the extconfig.conf since it was
714  * configured by res_pgsql.conf.
715  */
716  database = dbname;
717 
718  if (!tablename) {
719  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
720  return -1;
721  }
722 
723  if (!(table = find_table(database, tablename))) {
724  ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
725  return -1;
726  }
727 
728  /*
729  * Must connect to the server before anything else as ESCAPE_STRING()
730  * uses pgsqlConn
731  */
733  if (!pgsql_reconnect(database)) {
735  release_table(table);
736  return -1;
737  }
738 
739  /* Get the first parameter and first value in our list of passed paramater/value pairs */
740  if (!field) {
742  "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
743  if (pgsqlConn) {
744  PQfinish(pgsqlConn);
745  pgsqlConn = NULL;
746  }
748  release_table(table);
749  return -1;
750  }
751 
752  /* Check that the column exists in the table */
753  AST_LIST_TRAVERSE(&table->columns, column, list) {
754  if (strcmp(column->name, field->name) == 0) {
755  break;
756  }
757  }
758 
759  if (!column) {
760  ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", field->name, tablename);
762  release_table(table);
763  return -1;
764  }
765 
766  /* Create the first part of the query using the first parameter/value pairs we just extracted
767  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
768 
769  ESCAPE_STRING(escapebuf, field->value);
770  if (pgresult) {
771  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
773  release_table(table);
774  return -1;
775  }
776  ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, field->name, ast_str_buffer(escapebuf));
777 
778  while ((field = field->next)) {
779  if (!find_column(table, field->name)) {
780  ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", field->name, tablename);
781  continue;
782  }
783 
784  ESCAPE_STRING(escapebuf, field->value);
785  if (pgresult) {
786  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
788  release_table(table);
789  return -1;
790  }
791 
792  ast_str_append(&sql, 0, ", %s = '%s'", field->name, ast_str_buffer(escapebuf));
793  }
794  release_table(table);
795 
796  ESCAPE_STRING(escapebuf, lookup);
797  if (pgresult) {
798  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", lookup);
800  return -1;
801  }
802 
803  ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
804 
805  ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
806 
807  /* We now have our complete statement; Lets connect to the server and execute it. */
808  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
810  return -1;
811  } else {
812  ExecStatusType result_status = PQresultStatus(result);
813  if (result_status != PGRES_COMMAND_OK
814  && result_status != PGRES_TUPLES_OK
815  && result_status != PGRES_NONFATAL_ERROR) {
817  "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
818  ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
819  ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
820  PQresultErrorMessage(result), PQresStatus(result_status));
822  return -1;
823  }
824  }
825 
826  numrows = atoi(PQcmdTuples(result));
828 
829  ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
830 
831  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
832  * An integer greater than zero indicates the number of rows affected
833  * Zero indicates that no records were updated
834  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
835  */
836 
837  if (numrows >= 0)
838  return (int) numrows;
839 
840  return -1;
841 }
842 
843 static int update2_pgsql(const char *database, const char *tablename, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
844 {
845  RAII_VAR(PGresult *, result, NULL, PQclear);
846  int numrows = 0, pgresult, first = 1;
847  struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
848  const struct ast_variable *field;
849  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
850  struct ast_str *where = ast_str_thread_get(&where_buf, 100);
851  struct tables *table;
852 
853  /*
854  * Ignore database from the extconfig.conf since it was
855  * configured by res_pgsql.conf.
856  */
857  database = dbname;
858 
859  if (!tablename) {
860  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
861  return -1;
862  }
863 
864  if (!escapebuf || !sql || !where) {
865  /* Memory error, already handled */
866  return -1;
867  }
868 
869  if (!(table = find_table(database, tablename))) {
870  ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
871  return -1;
872  }
873 
874  /*
875  * Must connect to the server before anything else as ESCAPE_STRING()
876  * uses pgsqlConn
877  */
879  if (!pgsql_reconnect(database)) {
881  release_table(table);
882  return -1;
883  }
884 
885  ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
886  ast_str_set(&where, 0, " WHERE");
887 
888  for (field = lookup_fields; field; field = field->next) {
889  if (!find_column(table, field->name)) {
890  ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", field->name, tablename, database);
892  release_table(table);
893  return -1;
894  }
895 
896  ESCAPE_STRING(escapebuf, field->value);
897  if (pgresult) {
898  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
900  release_table(table);
901  return -1;
902  }
903  ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", field->name, ast_str_buffer(escapebuf));
904  first = 0;
905  }
906 
907  if (first) {
909  "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
910  if (pgsqlConn) {
911  PQfinish(pgsqlConn);
912  pgsqlConn = NULL;
913  }
915  release_table(table);
916  return -1;
917  }
918 
919  /* Now retrieve the columns to update */
920  first = 1;
921  for (field = update_fields; field; field = field->next) {
922  /* If the column is not within the table, then skip it */
923  if (!find_column(table, field->name)) {
924  ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", field->name, tablename, database);
925  continue;
926  }
927 
928  ESCAPE_STRING(escapebuf, field->value);
929  if (pgresult) {
930  ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", field->value);
932  release_table(table);
933  return -1;
934  }
935 
936  ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", field->name, ast_str_buffer(escapebuf));
937  first = 0;
938  }
939  release_table(table);
940 
941  ast_str_append(&sql, 0, "%s", ast_str_buffer(where));
942 
943  ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
944 
945  /* We now have our complete statement; Lets connect to the server and execute it. */
946  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
948  return -1;
949  }
950 
951  numrows = atoi(PQcmdTuples(result));
953 
954  ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
955 
956  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
957  * An integer greater than zero indicates the number of rows affected
958  * Zero indicates that no records were updated
959  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
960  */
961 
962  if (numrows >= 0) {
963  return (int) numrows;
964  }
965 
966  return -1;
967 }
968 
969 static int store_pgsql(const char *database, const char *table, const struct ast_variable *fields)
970 {
971  RAII_VAR(PGresult *, result, NULL, PQclear);
972  int numrows;
973  struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
974  struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
975  struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
976  int pgresult;
977  const struct ast_variable *field = fields;
978 
979  /*
980  * Ignore database from the extconfig.conf since it was
981  * configured by res_pgsql.conf.
982  */
983  database = dbname;
984 
985  if (!table) {
986  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
987  return -1;
988  }
989 
990  /*
991  * Must connect to the server before anything else as ESCAPE_STRING()
992  * uses pgsqlConn
993  */
995  if (!pgsql_reconnect(database)) {
997  return -1;
998  }
999 
1000  /* Get the first parameter and first value in our list of passed paramater/value pairs */
1001  if (!field) {
1003  "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
1004  if (pgsqlConn) {
1005  PQfinish(pgsqlConn);
1006  pgsqlConn = NULL;
1007  }
1009  return -1;
1010  }
1011 
1012  /* Create the first part of the query using the first parameter/value pairs we just extracted
1013  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1014  ESCAPE_STRING(buf, field->name);
1015  ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
1016  ESCAPE_STRING(buf, field->value);
1017  ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
1018  while ((field = field->next)) {
1019  ESCAPE_STRING(buf, field->name);
1020  ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
1021  ESCAPE_STRING(buf, field->value);
1022  ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
1023  }
1024  ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
1025 
1026  ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
1027 
1028  /* We now have our complete statement; Lets connect to the server and execute it. */
1029  if (pgsql_exec(database, table, ast_str_buffer(sql1), &result) != 0) {
1031  return -1;
1032  }
1033 
1034  numrows = atoi(PQcmdTuples(result));
1036 
1037  ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.\n", table);
1038 
1039  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1040  * An integer greater than zero indicates the number of rows affected
1041  * Zero indicates that no records were updated
1042  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1043  */
1044 
1045  if (numrows >= 0) {
1046  return numrows;
1047  }
1048 
1049  return -1;
1050 }
1051 
1052 static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
1053 {
1054  RAII_VAR(PGresult *, result, NULL, PQclear);
1055  int numrows = 0;
1056  int pgresult;
1057  struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
1059  const struct ast_variable *field;
1060 
1061  /*
1062  * Ignore database from the extconfig.conf since it was
1063  * configured by res_pgsql.conf.
1064  */
1065  database = dbname;
1066 
1067  if (!table) {
1068  ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
1069  return -1;
1070  }
1071 
1072  /*
1073  * Must connect to the server before anything else as ESCAPE_STRING()
1074  * uses pgsqlConn
1075  */
1077  if (!pgsql_reconnect(database)) {
1079  return -1;
1080  }
1081 
1082  /* Get the first parameter and first value in our list of passed paramater/value pairs */
1083  if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
1085  "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
1086  if (pgsqlConn) {
1087  PQfinish(pgsqlConn);
1088  pgsqlConn = NULL;
1089  }
1091  return -1;
1092  }
1093 
1094  /* Create the first part of the query using the first parameter/value pairs we just extracted
1095  If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1096 
1097  ESCAPE_STRING(buf1, keyfield);
1098  ESCAPE_STRING(buf2, lookup);
1099  ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
1100  for (field = fields; field; field = field->next) {
1101  ESCAPE_STRING(buf1, field->name);
1102  ESCAPE_STRING(buf2, field->value);
1103  ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
1104  }
1105 
1106  ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
1107 
1108  /* We now have our complete statement; Lets connect to the server and execute it. */
1109  if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
1111  return -1;
1112  }
1113 
1114  numrows = atoi(PQcmdTuples(result));
1116 
1117  ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
1118 
1119  /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-affected-rows.html
1120  * An integer greater than zero indicates the number of rows affected
1121  * Zero indicates that no records were updated
1122  * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1123  */
1124 
1125  if (numrows >= 0)
1126  return (int) numrows;
1127 
1128  return -1;
1129 }
1130 
1131 
1132 static struct ast_config *config_pgsql(const char *database, const char *table,
1133  const char *file, struct ast_config *cfg,
1134  struct ast_flags flags, const char *suggested_incl, const char *who_asked)
1135 {
1136  RAII_VAR(PGresult *, result, NULL, PQclear);
1137  long num_rows;
1138  struct ast_variable *new_v;
1139  struct ast_category *cur_cat = NULL;
1140  struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
1141  char last[80];
1142  int last_cat_metric = 0;
1143 
1144  last[0] = '\0';
1145 
1146  /*
1147  * Ignore database from the extconfig.conf since it is
1148  * configured by res_pgsql.conf.
1149  */
1150  database = dbname;
1151 
1152  if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
1153  ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
1154  return NULL;
1155  }
1156 
1157  ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
1158  "WHERE filename='%s' and commented=0 "
1159  "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
1160 
1161  ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
1162 
1164 
1165  /* We now have our complete statement; Lets connect to the server and execute it. */
1166  if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
1168  return NULL;
1169  }
1170 
1171  if ((num_rows = PQntuples(result)) > 0) {
1172  int rowIndex = 0;
1173 
1174  ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
1175 
1176  for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
1177  char *field_category = PQgetvalue(result, rowIndex, 0);
1178  char *field_var_name = PQgetvalue(result, rowIndex, 1);
1179  char *field_var_val = PQgetvalue(result, rowIndex, 2);
1180  char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
1181  if (!strcmp(field_var_name, "#include")) {
1182  if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
1184  return NULL;
1185  }
1186  continue;
1187  }
1188 
1189  if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
1190  cur_cat = ast_category_new_dynamic(field_category);
1191  if (!cur_cat) {
1192  break;
1193  }
1194  ast_copy_string(last, field_category, sizeof(last));
1195  last_cat_metric = atoi(field_cat_metric);
1196  ast_category_append(cfg, cur_cat);
1197  }
1198  new_v = ast_variable_new(field_var_name, field_var_val, "");
1199  ast_variable_append(cur_cat, new_v);
1200  }
1201  } else {
1203  "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
1204  }
1205 
1207 
1208  return cfg;
1209 }
1210 
1211 static int require_pgsql(const char *database, const char *tablename, va_list ap)
1212 {
1213  struct columns *column;
1214  struct tables *table;
1215  char *elm;
1216  int type, res = 0;
1217  unsigned int size;
1218 
1219  /*
1220  * Ignore database from the extconfig.conf since it was
1221  * configured by res_pgsql.conf.
1222  */
1223  database = dbname;
1224 
1225  table = find_table(database, tablename);
1226  if (!table) {
1227  ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
1228  return -1;
1229  }
1230 
1231  while ((elm = va_arg(ap, char *))) {
1232  type = va_arg(ap, require_type);
1233  size = va_arg(ap, unsigned int);
1234  AST_LIST_TRAVERSE(&table->columns, column, list) {
1235  if (strcmp(column->name, elm) == 0) {
1236  /* Char can hold anything, as long as it is large enough */
1237  if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
1238  if ((size > column->len) && column->len != -1) {
1239  ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
1240  res = -1;
1241  }
1242  } else if (strncmp(column->type, "int", 3) == 0) {
1243  int typesize = atoi(column->type + 3);
1244  /* Integers can hold only other integers */
1245  if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
1246  type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
1247  type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
1248  type == RQ_UINTEGER2) && typesize == 2) {
1249  ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
1250  res = -1;
1251  } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
1252  type == RQ_UINTEGER4) && typesize == 4) {
1253  ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
1254  res = -1;
1255  } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
1256  ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
1257  column->name,
1258  type == RQ_CHAR ? "char" :
1259  type == RQ_DATETIME ? "datetime" :
1260  type == RQ_DATE ? "date" :
1261  type == RQ_FLOAT ? "float" :
1262  "a rather stiff drink ",
1263  size, column->type);
1264  res = -1;
1265  }
1266  } else if (strncmp(column->type, "float", 5) == 0) {
1267  if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1268  ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
1269  res = -1;
1270  }
1271  } else if (strncmp(column->type, "timestamp", 9) == 0) {
1272  if (type != RQ_DATETIME && type != RQ_DATE) {
1273  ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
1274  res = -1;
1275  }
1276  } else { /* There are other types that no module implements yet */
1277  ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
1278  res = -1;
1279  }
1280  break;
1281  }
1282  }
1283 
1284  if (!column) {
1285  if (requirements == RQ_WARN) {
1286  ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
1287  res = -1;
1288  } else {
1289  struct ast_str *sql = ast_str_create(100);
1290  char fieldtype[10];
1291  PGresult *result;
1292 
1293  if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
1294  /* Size is minimum length; make it at least 50% greater,
1295  * just to be sure, because PostgreSQL doesn't support
1296  * resizing columns. */
1297  snprintf(fieldtype, sizeof(fieldtype), "CHAR(%u)",
1298  size < 15 ? size * 2 :
1299  (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
1300  } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
1301  snprintf(fieldtype, sizeof(fieldtype), "INT2");
1302  } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
1303  snprintf(fieldtype, sizeof(fieldtype), "INT4");
1304  } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
1305  snprintf(fieldtype, sizeof(fieldtype), "INT8");
1306  } else if (type == RQ_UINTEGER8) {
1307  /* No such type on PostgreSQL */
1308  snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
1309  } else if (type == RQ_FLOAT) {
1310  snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
1311  } else if (type == RQ_DATE) {
1312  snprintf(fieldtype, sizeof(fieldtype), "DATE");
1313  } else if (type == RQ_DATETIME) {
1314  snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
1315  } else {
1316  ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
1317  ast_free(sql);
1318  continue;
1319  }
1320  ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
1321  ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
1322 
1324  ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
1325 
1326  if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
1328  release_table(table);
1329  return -1;
1330  }
1331 
1332  ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
1333  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
1334  ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
1335  }
1336  PQclear(result);
1338 
1339  ast_free(sql);
1340  }
1341  }
1342  }
1343  release_table(table);
1344  return res;
1345 }
1346 
1347 static int unload_pgsql(const char *database, const char *tablename)
1348 {
1349  struct tables *cur;
1350 
1351  /*
1352  * Ignore database from the extconfig.conf since it was
1353  * configured by res_pgsql.conf.
1354  */
1355  database = dbname;
1356 
1357  ast_debug(2, "About to lock table cache list\n");
1359  ast_debug(2, "About to traverse table cache list\n");
1361  if (strcmp(cur->name, tablename) == 0) {
1362  ast_debug(2, "About to remove matching cache entry\n");
1364  ast_debug(2, "About to destroy matching cache entry\n");
1365  destroy_table(cur);
1366  ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
1367  break;
1368  }
1369  }
1372  ast_debug(2, "About to return\n");
1373  return cur ? 0 : -1;
1374 }
1375 
1377  .name = "pgsql",
1378  .load_func = config_pgsql,
1379  .realtime_func = realtime_pgsql,
1380  .realtime_multi_func = realtime_multi_pgsql,
1381  .store_func = store_pgsql,
1382  .destroy_func = destroy_pgsql,
1383  .update_func = update_pgsql,
1384  .update2_func = update2_pgsql,
1385  .require_func = require_pgsql,
1386  .unload_func = unload_pgsql,
1387 };
1388 
1389 static int load_module(void)
1390 {
1391  if(!parse_config(0))
1392  return AST_MODULE_LOAD_DECLINE;
1393 
1394  ast_config_engine_register(&pgsql_engine);
1395 
1396  ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
1397 
1398  return 0;
1399 }
1400 
1401 static int unload_module(void)
1402 {
1403  struct tables *table;
1404  /* Acquire control before doing anything to the module itself. */
1406 
1407  if (pgsqlConn) {
1408  PQfinish(pgsqlConn);
1409  pgsqlConn = NULL;
1410  }
1411  ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
1412  ast_config_engine_deregister(&pgsql_engine);
1413 
1414  /* Unlock so something else can destroy the lock. */
1416 
1417  /* Destroy cached table info */
1419  while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
1420  destroy_table(table);
1421  }
1423 
1424  return 0;
1425 }
1426 
1427 static int reload(void)
1428 {
1429  parse_config(1);
1430 
1431  return 0;
1432 }
1433 
1434 static int parse_config(int is_reload)
1435 {
1436  struct ast_config *config;
1437  const char *s;
1438  struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1439 
1440  config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
1441  if (config == CONFIG_STATUS_FILEUNCHANGED) {
1442  if (is_reload && pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
1443  ast_log(LOG_WARNING, "PostgreSQL RealTime: Not connected\n");
1444  }
1445  return 0;
1446  }
1447 
1448  if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
1449  ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
1450  return 0;
1451  }
1452 
1454 
1455  /* XXX: Why would we do this before we're ready to establish a new connection? */
1456  if (pgsqlConn) {
1457  PQfinish(pgsqlConn);
1458  pgsqlConn = NULL;
1459  }
1460 
1461  if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
1463  "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
1464  strcpy(dbuser, "asterisk");
1465  } else {
1466  ast_copy_string(dbuser, s, sizeof(dbuser));
1467  }
1468 
1469  if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
1471  "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
1472  strcpy(dbpass, "asterisk");
1473  } else {
1474  ast_copy_string(dbpass, s, sizeof(dbpass));
1475  }
1476 
1477  if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
1479  "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
1480  dbhost[0] = '\0';
1481  } else {
1482  ast_copy_string(dbhost, s, sizeof(dbhost));
1483  }
1484 
1485  if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
1487  "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
1488  strcpy(dbname, "asterisk");
1489  } else {
1490  ast_copy_string(dbname, s, sizeof(dbname));
1491  }
1492 
1493  if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
1495  "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
1496  dbport = 5432;
1497  } else {
1498  dbport = atoi(s);
1499  }
1500 
1501  if (!(s = ast_variable_retrieve(config, "general", "dbappname"))) {
1502  dbappname[0] = '\0';
1503  } else {
1504  ast_copy_string(dbappname, s, sizeof(dbappname));
1505  }
1506 
1507  if (!ast_strlen_zero(dbhost)) {
1508  /* No socket needed */
1509  } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
1511  "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
1512  strcpy(dbsock, "/tmp");
1513  } else {
1514  ast_copy_string(dbsock, s, sizeof(dbsock));
1515  }
1516 
1517  if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
1519  "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
1521  } else if (!strcasecmp(s, "createclose")) {
1523  } else if (!strcasecmp(s, "createchar")) {
1525  }
1526 
1527  ast_config_destroy(config);
1528 
1529  if (DEBUG_ATLEAST(1)) {
1530  if (!ast_strlen_zero(dbhost)) {
1531  ast_log(LOG_DEBUG, "PostgreSQL RealTime Host: %s\n", dbhost);
1532  ast_log(LOG_DEBUG, "PostgreSQL RealTime Port: %i\n", dbport);
1533  } else {
1534  ast_log(LOG_DEBUG, "PostgreSQL RealTime Socket: %s\n", dbsock);
1535  }
1536  ast_log(LOG_DEBUG, "PostgreSQL RealTime User: %s\n", dbuser);
1537  ast_log(LOG_DEBUG, "PostgreSQL RealTime Password: %s\n", dbpass);
1538  ast_log(LOG_DEBUG, "PostgreSQL RealTime DBName: %s\n", dbname);
1539  }
1540 
1541  if (!pgsql_reconnect(NULL)) {
1543  "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
1544  ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
1545  }
1546 
1547  ast_verb(2, "PostgreSQL RealTime reloaded.\n");
1548 
1549  /* Done reloading. Release lock so others can now use driver. */
1551 
1552  return 1;
1553 }
1554 
1555 static int pgsql_reconnect(const char *database)
1556 {
1557  char my_database[50];
1558 
1559  ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
1560 
1561  /* mutex lock should have been locked before calling this function. */
1562 
1563  if (pgsqlConn) {
1564  if (PQstatus(pgsqlConn) == CONNECTION_OK) {
1565  /* We're good? */
1566  return 1;
1567  }
1568 
1569  PQfinish(pgsqlConn);
1570  pgsqlConn = NULL;
1571  }
1572 
1573  /* DB password can legitimately be 0-length */
1574  if ((!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
1575  struct ast_str *conn_info = ast_str_create(128);
1576 
1577  if (!conn_info) {
1578  ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to allocate memory for connection string.\n");
1579  return 0;
1580  }
1581 
1582  ast_str_set(&conn_info, 0, "host=%s port=%d dbname=%s user=%s",
1583  S_OR(dbhost, dbsock), dbport, my_database, dbuser);
1584 
1585  if (!ast_strlen_zero(dbappname)) {
1586  ast_str_append(&conn_info, 0, " application_name=%s", dbappname);
1587  }
1588 
1589  if (!ast_strlen_zero(dbpass)) {
1590  ast_str_append(&conn_info, 0, " password=%s", dbpass);
1591  }
1592 
1593  pgsqlConn = PQconnectdb(ast_str_buffer(conn_info));
1594  ast_free(conn_info);
1595  conn_info = NULL;
1596 
1597  ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
1598  if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
1599  ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
1600  connect_time = time(NULL);
1601  version = PQserverVersion(pgsqlConn);
1602  return 1;
1603  } else {
1605  "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
1606  my_database, dbhost, PQresultErrorMessage(NULL));
1607  return 0;
1608  }
1609  } else {
1610  ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
1611  return 1;
1612  }
1613 }
1614 
1615 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1616 {
1617  struct tables *cur;
1618  int l, which;
1619  char *ret = NULL;
1620 
1621  switch (cmd) {
1622  case CLI_INIT:
1623  e->command = "realtime show pgsql cache";
1624  e->usage =
1625  "Usage: realtime show pgsql cache [<table>]\n"
1626  " Shows table cache for the PostgreSQL RealTime driver\n";
1627  return NULL;
1628  case CLI_GENERATE:
1629  if (a->argc != 4) {
1630  return NULL;
1631  }
1632  l = strlen(a->word);
1633  which = 0;
1636  if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
1637  ret = ast_strdup(cur->name);
1638  break;
1639  }
1640  }
1642  return ret;
1643  }
1644 
1645  if (a->argc == 4) {
1646  /* List of tables */
1649  ast_cli(a->fd, "%s\n", cur->name);
1650  }
1652  } else if (a->argc == 5) {
1653  /* List of columns */
1654  if ((cur = find_table(NULL, a->argv[4]))) {
1655  struct columns *col;
1656  ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
1657  ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
1658  AST_LIST_TRAVERSE(&cur->columns, col, list) {
1659  ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
1660  }
1661  release_table(cur);
1662  } else {
1663  ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
1664  }
1665  }
1666  return 0;
1667 }
1668 
1669 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1670 {
1671  char connection_info[256];
1672  char credentials[100] = "";
1673  char buf[376]; /* 256+100+"Connected to "+" for "+NULL */
1674  int is_connected = 0, ctimesec = time(NULL) - connect_time;
1675 
1676  switch (cmd) {
1677  case CLI_INIT:
1678  e->command = "realtime show pgsql status";
1679  e->usage =
1680  "Usage: realtime show pgsql status\n"
1681  " Shows connection information for the PostgreSQL RealTime driver\n";
1682  return NULL;
1683  case CLI_GENERATE:
1684  return NULL;
1685  }
1686 
1687  if (a->argc != 4)
1688  return CLI_SHOWUSAGE;
1689 
1690  if (!ast_strlen_zero(dbhost))
1691  snprintf(connection_info, sizeof(connection_info), "%s@%s, port %d", dbname, dbhost, dbport);
1692  else if (!ast_strlen_zero(dbsock))
1693  snprintf(connection_info, sizeof(connection_info), "%s on socket file %s", dbname, dbsock);
1694  else
1695  snprintf(connection_info, sizeof(connection_info), "%s@%s", dbname, dbhost);
1696 
1697  if (!ast_strlen_zero(dbuser))
1698  snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
1699 
1701  is_connected = (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK);
1703 
1704  if (is_connected) {
1705  snprintf(buf, sizeof(buf), "Connected to %s%s for ", connection_info, credentials);
1706  ast_cli_print_timestr_fromseconds(a->fd, ctimesec, buf);
1707  return CLI_SUCCESS;
1708  } else {
1709  ast_cli(a->fd, "Unable to connect %s%s\n", connection_info, credentials);
1710  return CLI_FAILURE;
1711  }
1712 }
1713 
1714 /* needs usecount semantics defined */
1715 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
1716  .support_level = AST_MODULE_SUPPORT_EXTENDED,
1717  .load = load_module,
1718  .unload = unload_module,
1719  .reload = reload,
1720  .load_pri = AST_MODPRI_REALTIME_DRIVER,
1721  .requires = "extconfig",
1722 );
unsigned int hasdefault
Definition: cdr_pgsql.c:98
#define has_schema_support
struct ast_variable * next
struct columns::@8 list
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:84
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)
require_type
Types used in ast_realtime_require_field.
struct column * last
Definition: cdr_mysql.c:107
char * ast_realtime_decode_chunk(char *chunk)
Remove standard encoding from realtime values, which ensures that a semicolon embedded within a singl...
Definition: main/config.c:3625
#define ast_rwlock_rdlock(a)
Definition: lock.h:233
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
#define ESCAPE_CLAUSE
static struct ast_variable * realtime_pgsql(const char *database, const char *tablename, const struct ast_variable *fields)
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void destroy_table(struct tables *table)
Asterisk locking-related definitions:
static struct ast_config * realtime_multi_pgsql(const char *database, const char *table, const struct ast_variable *fields)
Asterisk main include file. File version handling, generic pbx functions.
static int store_pgsql(const char *database, const char *table, const struct ast_variable *fields)
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
char * config
Definition: conf2ael.c:66
static int dbport
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static PGconn * pgsqlConn
static struct ast_threadstorage escapebuf_buf
#define ast_rwlock_destroy(rwlock)
Definition: lock.h:231
static char * handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char dbname[MAX_DB_OPTION_SIZE]
#define release_table(table)
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
#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:714
#define CONFIG_STATUS_FILEINVALID
Structure for variables, used for configurations and for channel variables.
static int unload_pgsql(const char *database, const char *tablename)
#define var
Definition: ast_expr2f.c:614
static int _pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Helper function for pgsql_exec. For running querys, use pgsql_exec()
static int update_pgsql(const char *database, const char *tablename, const char *keyfield, const char *lookup, const struct ast_variable *fields)
Definition: cli.h:152
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: main/config.c:3006
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
Definition: main/config.c:2990
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:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static struct ast_threadstorage buf2
#define ast_mutex_lock(a)
Definition: lock.h:187
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define NULL
Definition: resample.c:96
static ast_mutex_t pgsql_lock
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define LOG_DEBUG
Definition: logger.h:241
#define ast_rwlock_unlock(a)
Definition: lock.h:232
static char dbpass[MAX_DB_OPTION_SIZE]
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
static struct tables * find_table(const char *database, const char *orig_tablename)
#define ast_verb(level,...)
Definition: logger.h:463
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: main/config.c:3112
#define ESCAPE_STRING(buffer, stringname)
Configuration engine structure, used to define realtime drivers.
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
static enum @451 requirements
#define ast_category_new_anonymous()
Create a nameless category that is not backed by a file.
static char * table
Definition: cdr_odbc.c:58
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:1065
Configuration File Parser.
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1362
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define ast_config_load(filename, flags)
Load a config file.
#define RES_CONFIG_PGSQL_CONF
char name[0]
General Asterisk PBX channel definitions.
SQLINTEGER size
#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:911
const int fd
Definition: cli.h:159
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
const int n
Definition: cli.h:165
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static char dbappname[MAX_DB_OPTION_SIZE]
#define ast_variable_new(name, value, filename)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static int pgsql_reconnect(const char *database)
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3276
Core PBX routines and definitions.
#define CONFIG_STATUS_FILEUNCHANGED
#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
static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *fields)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char *const * argv
Definition: cli.h:161
static struct ast_cli_entry cli_realtime[]
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ast_rwlock_init(rwlock)
wrapper for rwlock with tracking enabled
Definition: lock.h:222
struct column * first
Definition: cdr_mysql.c:107
ast_rwlock_t lock
#define LOG_NOTICE
Definition: logger.h:263
#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
static struct columns columns
#define CLI_FAILURE
Definition: cli.h:46
struct mysql_conn * database
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
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:163
#define ast_category_new_dynamic(name)
Create a category that is not backed by a file.
requirements
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
Do a postgres query, with reconnection support.
static int version
static int update2_pgsql(const char *database, const char *tablename, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
Structure used to handle boolean flags.
Definition: utils.h:199
static struct ast_threadstorage sql_buf
static char dbuser[MAX_DB_OPTION_SIZE]
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
const char * usage
Definition: cli.h:177
void ast_cli_print_timestr_fromseconds(int fd, int seconds, const char *prefix)
Print on cli a duration in seconds in format s year(s), s week(s), s day(s), s hour(s), s second(s)
Definition: main/cli.c:3021
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1178
#define CONFIG_STATUS_FILEMISSING
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Appends a category to a config.
Definition: extconf.c:2835
#define ast_rwlock_wrlock(a)
Definition: lock.h:234
#define CLI_SUCCESS
Definition: cli.h:44
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
ast_mutex_t lock
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:680
unsigned int notnull
Definition: cdr_pgsql.c:97
char * strsep(char **str, const char *delims)
Structure for rwlock and tracking information.
Definition: lock.h:156
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static PGresult * result
Definition: cel_pgsql.c:88
struct tables::mysql_columns columns
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:861
static struct ast_config_engine pgsql_engine
static time_t connect_time
int ast_rq_is_int(require_type type)
Check if require type is an integer type.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define DEBUG_ATLEAST(level)
Definition: logger.h:441
static char dbhost[MAX_DB_OPTION_SIZE]
Asterisk module definitions.
#define IS_SQL_LIKE_CLAUSE(x)
static int load_module(void)
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:518
#define MAX_DB_OPTION_SIZE
static int unload_module(void)
static int reload(void)
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
#define ast_mutex_unlock(a)
Definition: lock.h:188
static struct test_val a